ethereum.go 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559
  1. package ethereum
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "math/big"
  7. "os"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "github.com/ditcraft/client/config"
  12. "github.com/ditcraft/client/git"
  13. "github.com/ditcraft/client/helpers"
  14. "github.com/ditcraft/client/smartcontracts/KNWToken"
  15. "github.com/ditcraft/client/smartcontracts/KNWVoting"
  16. "github.com/ditcraft/client/smartcontracts/ditCoordinator"
  17. "github.com/ditcraft/client/smartcontracts/ditDemoCoordinator"
  18. "github.com/ditcraft/client/smartcontracts/ditToken"
  19. "github.com/ethereum/go-ethereum/accounts/abi"
  20. "github.com/ethereum/go-ethereum/accounts/abi/bind"
  21. "github.com/ethereum/go-ethereum/common"
  22. "github.com/ethereum/go-ethereum/crypto"
  23. "github.com/ethereum/go-ethereum/ethclient"
  24. )
  25. const correctETHAddressLength = 42
  26. // SetDitCoordinator will write the dit coordinators address to the config and
  27. // retrieve the addresses of the KNWToken and KNWVoting contracts
  28. func SetDitCoordinator(_ditCoordinatorAddressString string) error {
  29. connection, err := getConnection()
  30. if err != nil {
  31. return err
  32. }
  33. // Create a new instance of the ditCoordinator to access it
  34. ditCoordinatorInstance, err := getDitCoordinatorInstance(connection, _ditCoordinatorAddressString)
  35. if err != nil {
  36. return err
  37. }
  38. // Retrieving the KNWVoting contracts address from the ditCoordinator
  39. KNWVotingAddress, err := ditCoordinatorInstance.KNWVotingAddress(nil)
  40. if err != nil {
  41. return errors.New("Failed to retrieve address of the KNWVoting contract")
  42. }
  43. // Check whether the retrieved address is valid (i.e. not zero)
  44. if KNWVotingAddress == common.HexToAddress("0") {
  45. return errors.New("Received an invalid address of the KNWVoting contract")
  46. }
  47. // Retrieving the KNWToken contracts address from the ditCoordinator
  48. KNWTokenAddress, err := ditCoordinatorInstance.KNWTokenAddress(nil)
  49. if err != nil {
  50. return errors.New("Failed to retrieve address of the KNWToken contract")
  51. }
  52. // Check whether the retrieved address is valid (i.e. not zero)
  53. if KNWTokenAddress == common.HexToAddress("0") {
  54. return errors.New("Received an invalid address of the KNWToken contract")
  55. }
  56. // If in demo mode, also retrieve the xDit token address
  57. if config.DitConfig.DemoModeActive {
  58. // Create a new instance of the ditDemoCoordinator to access it
  59. ditDemoCoordinatorInstance, err := getDitDemoCoordinatorInstance(connection, _ditCoordinatorAddressString)
  60. if err != nil {
  61. return err
  62. }
  63. // Retrieving the ditToken contracts address from the ditDemoCoordinator
  64. ditTokenAddress, err := ditDemoCoordinatorInstance.XDitTokenAddress(nil)
  65. if err != nil {
  66. return errors.New("Failed to retrieve address of the KNWToken contract")
  67. }
  68. config.DitConfig.DitToken = ditTokenAddress.Hex()
  69. config.DitConfig.Currency = "xDit"
  70. } else {
  71. config.DitConfig.DitToken = ""
  72. config.DitConfig.Currency = "xDai"
  73. }
  74. // Setting the retrieved addresses to the config object
  75. config.DitConfig.DitCoordinator = _ditCoordinatorAddressString
  76. config.DitConfig.KNWVoting = KNWVotingAddress.Hex()
  77. config.DitConfig.KNWToken = KNWTokenAddress.Hex()
  78. config.DitConfig.PassedKYC = false
  79. // Saving the config back to the file
  80. err = config.Save()
  81. if err != nil {
  82. return nil
  83. }
  84. return nil
  85. }
  86. // InitDitRepository will initialize a new repository and store its ditContracts address
  87. // together with additional information into the config
  88. // When there is no ditContract for this repository, the user will be prompted to deploy one
  89. func InitDitRepository(_optionalRepository ...string) error {
  90. if !config.DitConfig.PassedKYC {
  91. passedKYC, err := CheckForKYC()
  92. if err != nil {
  93. return err
  94. } else if !passedKYC {
  95. return errors.New("You didn't pass the KYC yet")
  96. }
  97. }
  98. var repository string
  99. // If a repository name is already provided with the call of this function we will use it...
  100. if len(_optionalRepository) > 0 {
  101. repository = _optionalRepository[0]
  102. } else {
  103. // ...otherwise we will retrieve the name of this repository
  104. var err error
  105. repository, err = git.GetRepository()
  106. if err != nil {
  107. return err
  108. }
  109. }
  110. var repositoryArray []config.Repository
  111. if config.DitConfig.DemoModeActive {
  112. repositoryArray = config.DitConfig.DemoRepositories
  113. } else {
  114. repositoryArray = config.DitConfig.LiveRepositories
  115. }
  116. // Searching through the repositories that are in our config
  117. for i := 0; i < len(repositoryArray); i++ {
  118. // If the repository was found in our config it has already been initialized
  119. // In that case we don't need to proceed further
  120. if repositoryArray[i].Name == repository {
  121. if len(_optionalRepository) > 0 {
  122. return nil
  123. }
  124. helpers.PrintLine("Repository is already initialized", 0)
  125. os.Exit(0)
  126. }
  127. }
  128. connection, err := getConnection()
  129. if err != nil {
  130. return err
  131. }
  132. repoHash := GetHashOfString(repository)
  133. // Create a new instance of the ditCoordinator to access it
  134. ditCoordinatorInstance, err := getDitCoordinatorInstance(connection)
  135. if err != nil {
  136. return err
  137. }
  138. // Retrieving the address of the ditContract that was deployed for this repository
  139. isInitialized, err := ditCoordinatorInstance.RepositoryIsInitialized(nil, repoHash)
  140. if err != nil {
  141. return err
  142. }
  143. // If the address is zero, there is no contract deployed
  144. if !isInitialized {
  145. // Prompt the user whether he wants to deploy one
  146. answer := helpers.GetUserInputChoice("This repository hasn't been initialized yet, do you want to initialize it?", "y", "n")
  147. if answer == "y" {
  148. // If yes: deploy the ditContract
  149. err = initDitRepository(ditCoordinatorInstance, repoHash)
  150. if err != nil {
  151. return err
  152. }
  153. } else {
  154. // If not: exit
  155. helpers.PrintLine("No ditContract deployed - repository can't be used with dit", 1)
  156. os.Exit(0)
  157. }
  158. }
  159. // Retrieving the knowledge-labels of this ditContract
  160. var knowledgeLabels []string
  161. for i := 0; i < 3; i++ {
  162. contractKnowledgeLabels, err := ditCoordinatorInstance.GetKnowledgeLabels(nil, repoHash, big.NewInt(int64(i)))
  163. if err != nil {
  164. return err
  165. }
  166. if len(contractKnowledgeLabels) > 0 {
  167. knowledgeLabels = append(knowledgeLabels, contractKnowledgeLabels)
  168. }
  169. }
  170. // Inserting this repositories details into the cobfig
  171. var newRepository config.Repository
  172. newRepository.Name = repository
  173. if strings.Contains(repository, "github.com") {
  174. newRepository.Provider = "github"
  175. }
  176. newRepository.KnowledgeLabels = knowledgeLabels
  177. newRepository.ActiveVotes = make([]config.ActiveVote, 0)
  178. repositoryArray = append(repositoryArray, newRepository)
  179. if config.DitConfig.DemoModeActive {
  180. config.DitConfig.DemoRepositories = repositoryArray
  181. } else {
  182. config.DitConfig.LiveRepositories = repositoryArray
  183. }
  184. // Saving the config back to the file
  185. err = config.Save()
  186. if err != nil {
  187. return nil
  188. }
  189. return nil
  190. }
  191. // ProposeCommit will start a new proposal on the ditContract of this repository
  192. func ProposeCommit(_commitMessage string) (string, int, error) {
  193. if !config.DitConfig.PassedKYC {
  194. passedKYC, err := CheckForKYC()
  195. if err != nil {
  196. return "", 0, err
  197. } else if !passedKYC {
  198. return "", 0, errors.New("You didn't pass the KYC yet")
  199. }
  200. }
  201. // Searching for this repositories object in the config
  202. repoIndex, err := searchForRepoInConfig()
  203. if err != nil {
  204. return "", 0, err
  205. }
  206. repoHash := GetHashOfString(config.DitConfig.LiveRepositories[repoIndex].Name)
  207. // Gathering the the knowledge-labels from the config
  208. knowledgeLabels := config.DitConfig.LiveRepositories[repoIndex].KnowledgeLabels
  209. connection, err := getConnection()
  210. if err != nil {
  211. return "", 0, err
  212. }
  213. // Create a new instance of the ditContract to access it
  214. ditCoordinatorInstance, err := getDitCoordinatorInstance(connection)
  215. if err != nil {
  216. return "", 0, err
  217. }
  218. // Convertig the hex-string-formatted address into address object
  219. myAddress := common.HexToAddress(config.DitConfig.EthereumKeys.Address)
  220. // Prompting the user which knowledge-label he wants to use for this proposal
  221. answerKnowledgeLabel := 0
  222. userInputString := "Which Knowledge-Label suits this commit most?"
  223. for i := range knowledgeLabels {
  224. userInputString += " (" + strconv.Itoa(i+1) + ") " + knowledgeLabels[i]
  225. }
  226. for answerKnowledgeLabel < 1 || answerKnowledgeLabel > len(knowledgeLabels) {
  227. answerKnowledgeLabel, _ = strconv.Atoi(helpers.GetUserInput(userInputString))
  228. }
  229. // Retrieving the xDai balance of the user
  230. xDaiBalance, err := connection.BalanceAt(context.Background(), myAddress, nil)
  231. if err != nil {
  232. return "", 0, errors.New("Failed to retrieve " + config.DitConfig.Currency + " balance")
  233. }
  234. // Formatting the xDai balance to a human-readable format
  235. floatBalance := new(big.Float).Quo((new(big.Float).SetInt(xDaiBalance)), big.NewFloat(1000000000000000000))
  236. // Prompting the user how much stake he wants to set for this proposal
  237. answerStake := "0"
  238. floatStakeParsed, _ := strconv.ParseFloat(answerStake, 64)
  239. floatStake := big.NewFloat(floatStakeParsed)
  240. helpers.PrintLine(fmt.Sprintf("You have a balance of %.2f xDai", floatBalance), 0)
  241. userInputString = fmt.Sprintf("How much do you want to stake?")
  242. for floatStake.Cmp(big.NewFloat(0)) == 0 || floatStake.Cmp(floatBalance) != -1 {
  243. answerStake = helpers.GetUserInput(userInputString)
  244. floatStakeParsed, _ = strconv.ParseFloat(answerStake, 64)
  245. floatStake = big.NewFloat(floatStakeParsed)
  246. }
  247. // Prompting the user whether he is sure of this proposal and its details
  248. floatStakeString := fmt.Sprintf("%.2f", floatStakeParsed)
  249. helpers.PrintLine(" Proposing the commit with the following settings:", 0)
  250. helpers.PrintLine(" Commit Message: "+_commitMessage+"", 0)
  251. helpers.PrintLine(" Knowledge Label: "+knowledgeLabels[answerKnowledgeLabel-1], 0)
  252. helpers.PrintLine(" The following stake with automatically be deducted: "+floatStakeString+"xDai", 0)
  253. userIsSure := helpers.GetUserInputChoice("Is that correct?", "y", "n")
  254. if userIsSure == "n" {
  255. return "", 0, errors.New("Canceled proposal of commit due to users choice")
  256. }
  257. fmt.Println()
  258. // Crerating the transaction (basic values)
  259. auth, err := populateTx(connection)
  260. if err != nil {
  261. return "", 0, err
  262. }
  263. // Setting the value of the transaction to be the selected stake
  264. weiFloatStake, _ := (new(big.Float).Mul(floatStake, big.NewFloat(1000000000000000000))).Int64()
  265. intStake := big.NewInt(weiFloatStake)
  266. auth.Value = intStake
  267. // Retrieving the last/current proposalID of the ditContract
  268. // (This will increment after a proposal, so we can see when the proposal is live)
  269. lastProposalID, err := ditCoordinatorInstance.GetCurrentProposalID(nil, repoHash)
  270. if err != nil {
  271. return "", 0, errors.New("Failed to retrieve the current proposal id")
  272. }
  273. // Proposing the commit
  274. transaction, err := ditCoordinatorInstance.ProposeCommit(auth, repoHash, big.NewInt(int64(answerKnowledgeLabel-1)), big.NewInt(int64(180)), big.NewInt(int64(180)))
  275. if err != nil {
  276. if strings.Contains(err.Error(), "insufficient funds") {
  277. return "", 0, errors.New("Your account doesn't have enough xDai to pay for the transaction")
  278. }
  279. return "", 0, err
  280. }
  281. // Waiting for the proposals transaction to be mined
  282. helpers.Printf("Waiting for commit proposal transaction to be mined", 0)
  283. newProposalID := lastProposalID
  284. waitingFor := 0
  285. for newProposalID.Cmp(lastProposalID) == 0 {
  286. waitingFor += 5
  287. time.Sleep(5 * time.Second)
  288. fmt.Printf(".")
  289. // Checking the current proposal ID every 5 seconds
  290. newProposalID, err = ditCoordinatorInstance.GetCurrentProposalID(nil, repoHash)
  291. if err != nil {
  292. return "", 0, errors.New("Failed to retrieve the current proposal id")
  293. }
  294. // If we are waiting for more than 2 minutes, the transaction might have failed
  295. if waitingFor > 180 {
  296. fmt.Printf("\n")
  297. helpers.PrintLine("Waiting for over 3 minutes, maybe the transaction or the network failed?", 1)
  298. helpers.PrintLine("Check at: https://blockscout.com/poa/dai/tx/"+transaction.Hash().Hex(), 1)
  299. os.Exit(0)
  300. }
  301. }
  302. fmt.Printf("\n")
  303. // Gathering the information of the new proposal from the ditContract (including the times to commit and reveal)
  304. newVote, err := gatherProposalInfo(connection, ditCoordinatorInstance, repoHash, newProposalID.Int64())
  305. if err != nil {
  306. return "", 0, err
  307. }
  308. // Adding the new vote to the config object
  309. config.DitConfig.LiveRepositories[repoIndex].ActiveVotes = append(config.DitConfig.LiveRepositories[repoIndex].ActiveVotes, newVote)
  310. // Saving the config back to the file
  311. err = config.Save()
  312. if err != nil {
  313. return "", 0, err
  314. }
  315. // Conwerting the stake and used KNW count into a float so that it's human-readable
  316. floatxDai := new(big.Float).Quo((new(big.Float).SetInt(big.NewInt(int64(newVote.NumTokens)))), big.NewFloat(1000000000000000000))
  317. floatKNW := new(big.Float).Quo((new(big.Float).SetInt(big.NewInt(int64(newVote.NumKNW)))), big.NewFloat(1000000000000000000))
  318. // Formatting the time of the commit and reveal phase into a readable format
  319. timeReveal := time.Unix(int64(newVote.RevealEnd), 0)
  320. timeRevealString := timeReveal.Format("15:04:05 on 2006/01/02")
  321. timeCommit := time.Unix(int64(newVote.CommitEnd), 0)
  322. timeCommitString := timeCommit.Format("15:04:05 on 2006/01/02")
  323. // Returning the response to the user, we don't want to print this right here and now
  324. // since we are not sure whether the actual git commands will succeed.
  325. // So it will be printed afterwards in the main routine
  326. var responseString string
  327. responseString += "---------------------------"
  328. responseString += "\nSuccessfully proposed commit. Vote on proposal started with ID " + strconv.Itoa(int(newVote.ID)) + ""
  329. responseString += fmt.Sprintf("\nYou staked %.2f %s and used %f KNW.", floatxDai, config.DitConfig.Currency, floatKNW)
  330. responseString += "\nThe vote will end at " + timeRevealString
  331. if config.DitConfig.LiveRepositories[repoIndex].Provider == "github" {
  332. responseString += "\nYour commit is at https://github.com/" + config.DitConfig.LiveRepositories[repoIndex].Name + "/tree/dit_proposal_" + strconv.Itoa(int(newVote.ID))
  333. }
  334. responseString += "\n---------------------------"
  335. if config.DitConfig.DemoModeActive {
  336. fmt.Println()
  337. helpers.PrintLine("You may now simulate demo voters with '"+helpers.ColorizeCommand("demo_vote "+strconv.Itoa(int(newVote.ID)))+"' until "+timeCommitString, 3)
  338. fmt.Println()
  339. }
  340. return responseString, int(newProposalID.Int64()), nil
  341. }
  342. // Vote will cast a users vote on a proposal
  343. func Vote(_proposalID string, _choice string, _salt string) error {
  344. if !config.DitConfig.PassedKYC {
  345. passedKYC, err := CheckForKYC()
  346. if err != nil {
  347. return err
  348. } else if !passedKYC {
  349. return errors.New("You didn't pass the KYC yet")
  350. }
  351. }
  352. // Converting the stdin string input of the user into Ints
  353. proposalID, _ := strconv.Atoi(_proposalID)
  354. choice, _ := strconv.Atoi(_choice)
  355. salt, _ := strconv.Atoi(_salt)
  356. // Searching for this repositories object in the config
  357. repoIndex, err := searchForRepoInConfig()
  358. if err != nil {
  359. return err
  360. }
  361. var repositoryArray []config.Repository
  362. if config.DitConfig.DemoModeActive {
  363. repositoryArray = config.DitConfig.DemoRepositories
  364. } else {
  365. repositoryArray = config.DitConfig.LiveRepositories
  366. }
  367. repoHash := GetHashOfString(repositoryArray[repoIndex].Name)
  368. connection, err := getConnection()
  369. if err != nil {
  370. return err
  371. }
  372. // Convertig the hex-string-formatted address into address object
  373. myAddress := common.HexToAddress(config.DitConfig.EthereumKeys.Address)
  374. // Create a new instance of the ditContract to access it
  375. ditCooordinatorInstance, err := getDitCoordinatorInstance(connection)
  376. if err != nil {
  377. return err
  378. }
  379. // Create a new instance of the KNWVoting contract to access it
  380. KNWVotingInstance, err := getKNWVotingInstance(connection)
  381. if err != nil {
  382. return err
  383. }
  384. // Retrieving the proposal object from the ditContract
  385. proposal, err := ditCooordinatorInstance.ProposalsOfRepository(nil, repoHash, big.NewInt(int64(proposalID)))
  386. if err != nil {
  387. return errors.New("Failed to retrieve proposal")
  388. }
  389. // Verifiying whether the proposal is valid (if KNWVoteID is zero its not valid or non existent)
  390. if proposal.KNWVoteID.Int64() == 0 {
  391. return errors.New("Invalid proposalID")
  392. }
  393. if proposal.Proposer == myAddress {
  394. return errors.New("You can't vote on your own proposal")
  395. }
  396. // Retrieving the default stake from the ditContract
  397. requiredStake, err := KNWVotingInstance.GetGrossStake(nil, proposal.KNWVoteID)
  398. if err != nil {
  399. return errors.New("Failed to retrieve the required stake of the vote")
  400. }
  401. floatStake := new(big.Float).Quo((new(big.Float).SetInt(requiredStake)), big.NewFloat(1000000000000000000))
  402. helpers.PrintLine("Voting on this proposal will automatically deduct the required stake from you account.", 0)
  403. helpers.PrintLine(fmt.Sprintf("Required stake: %.2f", floatStake), 0)
  404. helpers.PrintLine("All participants of the vote will counter-stake the proposer.", 0)
  405. helpers.PrintLine("You will receive the remaining stake back, no matter how the vote ends.", 0)
  406. answer := helpers.GetUserInputChoice("Is this okay for you?", "y", "n")
  407. if answer == "n" {
  408. // If not: exit
  409. helpers.PrintLine("No vote executed due to users choice", 1)
  410. os.Exit(0)
  411. }
  412. // In order to create a valid abi-encoded hash of the vote choice and salt
  413. // we need to create an abi object
  414. uint256Type, _ := abi.NewType("uint256")
  415. arguments := abi.Arguments{
  416. {
  417. Type: uint256Type,
  418. },
  419. {
  420. Type: uint256Type,
  421. },
  422. }
  423. // We will now put pack this abi object into a bytearray
  424. bytes, _ := arguments.Pack(
  425. big.NewInt(int64(choice)),
  426. big.NewInt(int64(salt)),
  427. )
  428. // And finally hash this bytearray with keccak256, resulting in the votehash
  429. voteHash := crypto.Keccak256Hash(bytes)
  430. // Verifying whether the commit period of this vote is active
  431. commitPeriodActive, err := KNWVotingInstance.CommitPeriodActive(nil, proposal.KNWVoteID)
  432. if err != nil {
  433. return errors.New("Failed to retrieve opening status")
  434. }
  435. // If it is now active it's probably over
  436. if !commitPeriodActive {
  437. return errors.New("The commit phase of this vote has ended")
  438. }
  439. // Verifying whether the user has already commited a vote on this proposal
  440. oldDidCommit, err := KNWVotingInstance.DidCommit(nil, myAddress, proposal.KNWVoteID)
  441. if err != nil {
  442. return errors.New("Failed to retrieve commit status")
  443. }
  444. // If this is the case, the user can not vote again
  445. if oldDidCommit {
  446. return errors.New("You already voted on this proposal")
  447. }
  448. // Crerating the transaction (basic values)
  449. auth, err := populateTx(connection)
  450. if err != nil {
  451. return err
  452. }
  453. // Setting the value of the transaction to be the default stake
  454. auth.Value = requiredStake
  455. // Voting on the proposal
  456. transaction, err := ditCooordinatorInstance.VoteOnProposal(auth, repoHash, big.NewInt(int64(proposalID)), voteHash)
  457. if err != nil {
  458. if strings.Contains(err.Error(), "insufficient funds") {
  459. return errors.New("Your account doesn't have enough xDai to pay for the transaction")
  460. }
  461. return errors.New("Failed to commit the vote: " + err.Error())
  462. }
  463. // Waiting for the voting transaction to be mined
  464. helpers.Printf("Waiting for voting transaction to be mined", 0)
  465. waitingFor := 0
  466. newDidCommit := oldDidCommit
  467. for newDidCommit == oldDidCommit {
  468. waitingFor += 5
  469. time.Sleep(5 * time.Second)
  470. fmt.Printf(".")
  471. // Checking the commit status of the user every 5 seconds
  472. newDidCommit, err = KNWVotingInstance.DidCommit(nil, myAddress, proposal.KNWVoteID)
  473. if err != nil {
  474. return errors.New("Failed to retrieve commit status")
  475. }
  476. // If we are waiting for more than 2 minutes, the transaction might have failed
  477. if waitingFor > 180 {
  478. fmt.Printf("\n")
  479. helpers.PrintLine("Waiting for over 3 minutes, maybe the transaction or the network failed?", 1)
  480. helpers.PrintLine("Check at: https://blockscout.com/poa/dai/tx/"+transaction.Hash().Hex(), 1)
  481. os.Exit(0)
  482. }
  483. }
  484. fmt.Printf("\n")
  485. // Gathering the information of the proposal from the ditContract
  486. newVote, err := gatherProposalInfo(connection, ditCooordinatorInstance, repoHash, int64(proposalID))
  487. if err != nil {
  488. return err
  489. }
  490. // We will also store the users choice and salt, so that the user doesn't need to remember the salt
  491. // when he will reveal the vote later on
  492. newVote.Choice = choice
  493. newVote.Salt = salt
  494. // Adding the new vote to the config object
  495. repositoryArray[repoIndex].ActiveVotes = append(repositoryArray[repoIndex].ActiveVotes, newVote)
  496. if config.DitConfig.DemoModeActive {
  497. config.DitConfig.DemoRepositories = repositoryArray
  498. } else {
  499. config.DitConfig.LiveRepositories = repositoryArray
  500. }
  501. // Saving the config back to the file
  502. err = config.Save()
  503. if err != nil {
  504. return err
  505. }
  506. // Formatting the time of the commit and reveal phase into a readable format
  507. timeCommit := time.Unix(int64(newVote.CommitEnd), 0)
  508. timeCommitString := timeCommit.Format("15:04:05 on 2006/01/02")
  509. timeReveal := time.Unix(int64(newVote.RevealEnd), 0)
  510. timeRevealString := timeReveal.Format("15:04:05 on 2006/01/02")
  511. if choice == 1 {
  512. helpers.PrintLine("You successfully voted in favor of the proposal", 0)
  513. } else {
  514. helpers.PrintLine("You successfully voted against the proposal", 0)
  515. }
  516. // Letting the user know when and how he has to reveal the vote
  517. helpers.PrintLine("With dit, votes are casted in a concealed manner through a commitment scheme.", 0)
  518. helpers.PrintLine("This means that votes have to be revealed to the public once the commit-phase is over.", 0)
  519. helpers.PrintLine("Please open your vote with '"+helpers.ColorizeCommand("open "+strconv.Itoa(int(proposalID)))+"' between "+timeCommitString+" and "+timeRevealString, 0)
  520. return nil
  521. }
  522. // Open will open and thus reveal a vote (that was commited through a hash) to the public during the opening phase
  523. func Open(_proposalID string) error {
  524. if !config.DitConfig.PassedKYC {
  525. passedKYC, err := CheckForKYC()
  526. if err != nil {
  527. return err
  528. } else if !passedKYC {
  529. return errors.New("You didn't pass the KYC yet")
  530. }
  531. }
  532. // Converting the stdin string input of the user into an int
  533. proposalID, _ := strconv.Atoi(_proposalID)
  534. // Searching for this repositories object in the config
  535. repoIndex, err := searchForRepoInConfig()
  536. if err != nil {
  537. return err
  538. }
  539. var repositoryArray []config.Repository
  540. if config.DitConfig.DemoModeActive {
  541. repositoryArray = config.DitConfig.DemoRepositories
  542. } else {
  543. repositoryArray = config.DitConfig.LiveRepositories
  544. }
  545. repoHash := GetHashOfString(repositoryArray[repoIndex].Name)
  546. connection, err := getConnection()
  547. if err != nil {
  548. return err
  549. }
  550. // Convertig the hex-string-formatted address into an address object
  551. myAddress := common.HexToAddress(config.DitConfig.EthereumKeys.Address)
  552. // Create a new instance of the ditContract to access it
  553. ditCooordinatorInstance, err := getDitCoordinatorInstance(connection)
  554. if err != nil {
  555. return err
  556. }
  557. // Create a new instance of the KNWVoting contract to access it
  558. KNWVotingInstance, err := getKNWVotingInstance(connection)
  559. if err != nil {
  560. return err
  561. }
  562. // Searching for the corresponding vote in the votes stored in the config
  563. var voteIndex int
  564. for i := range repositoryArray[repoIndex].ActiveVotes {
  565. if repositoryArray[repoIndex].ActiveVotes[i].ID == proposalID {
  566. voteIndex = i
  567. break
  568. }
  569. }
  570. // Verifying whether the reveal period of this vote is active
  571. revealPeriodActive, err := KNWVotingInstance.RevealPeriodActive(nil, big.NewInt(int64(repositoryArray[repoIndex].ActiveVotes[voteIndex].KNWVoteID)))
  572. if err != nil {
  573. return errors.New("Failed to retrieve opening status")
  574. }
  575. // If it is now active it hasn't started yet or it's over
  576. if !revealPeriodActive {
  577. return errors.New("The opening phase of this vote is not active")
  578. }
  579. // Verifying whether the user has commited a vote on this proposal
  580. didCommit, err := KNWVotingInstance.DidCommit(nil, myAddress, big.NewInt(int64(repositoryArray[repoIndex].ActiveVotes[voteIndex].KNWVoteID)))
  581. if err != nil {
  582. return errors.New("Failed to retrieve commit status")
  583. }
  584. // If this is not the case the user never participated in this proposal through a vote
  585. if !didCommit {
  586. return errors.New("You didn't vote on this proposal")
  587. }
  588. // Verifying whether the user has revealed his vote on this proposal
  589. oldDidReveal, err := KNWVotingInstance.DidReveal(nil, myAddress, big.NewInt(int64(repositoryArray[repoIndex].ActiveVotes[voteIndex].KNWVoteID)))
  590. if err != nil {
  591. return errors.New("Failed to retrieve opening status")
  592. }
  593. // If this is the case, the user already revealed his vote
  594. if oldDidReveal {
  595. return errors.New("You already opened your vote on this proposal")
  596. }
  597. // Crerating the transaction (basic values)
  598. auth, err := populateTx(connection)
  599. if err != nil {
  600. return err
  601. }
  602. // Gathering the original choice and the salt from the config
  603. choice := big.NewInt(int64(repositoryArray[repoIndex].ActiveVotes[voteIndex].Choice))
  604. salt := big.NewInt(int64(repositoryArray[repoIndex].ActiveVotes[voteIndex].Salt))
  605. // Revealing the vote on the proposal
  606. transaction, err := ditCooordinatorInstance.OpenVoteOnProposal(auth, repoHash, big.NewInt(int64(proposalID)), choice, salt)
  607. if err != nil {
  608. if strings.Contains(err.Error(), "insufficient funds") {
  609. return errors.New("Your account doesn't have enough xDai to pay for the transaction")
  610. }
  611. return errors.New("Failed to open the vote: " + err.Error())
  612. }
  613. // Waiting for the reveal transaction to be mined
  614. helpers.Printf("Waiting for opening transaction to be mined", 0)
  615. waitingFor := 0
  616. newDidReveal := oldDidReveal
  617. for newDidReveal == oldDidReveal {
  618. waitingFor += 5
  619. time.Sleep(5 * time.Second)
  620. fmt.Printf(".")
  621. // Checking the reveal status of the user every 5 seconds
  622. newDidReveal, err = KNWVotingInstance.DidReveal(nil, myAddress, big.NewInt(int64(repositoryArray[repoIndex].ActiveVotes[voteIndex].KNWVoteID)))
  623. if err != nil {
  624. return errors.New("Failed to retrieve opening status")
  625. }
  626. // If we are waiting for more than 2 minutes, the transaction might have failed
  627. if waitingFor > 180 {
  628. fmt.Printf("\n")
  629. helpers.PrintLine("Waiting for over 3 minutes, maybe the transaction or the network failed?", 1)
  630. helpers.PrintLine("Check at: https://blockscout.com/poa/dai/tx/"+transaction.Hash().Hex(), 1)
  631. os.Exit(0)
  632. }
  633. }
  634. fmt.Printf("\n")
  635. // Formatting the time of the reveal phase into a readable format
  636. timeReveal := time.Unix(int64(repositoryArray[repoIndex].ActiveVotes[voteIndex].RevealEnd), 0)
  637. timeRevealString := timeReveal.Format("15:04:05 on 2006/01/02")
  638. helpers.PrintLine("Successfully opened your vote", 0)
  639. helpers.PrintLine("To finalize it when the vote is over, execute '"+helpers.ColorizeCommand("finalize "+_proposalID)+"' after "+timeRevealString, 3)
  640. return nil
  641. }
  642. // Finalize will finalize a vote as it will trigger the calculation of the reward of this user
  643. // including the xDai and KNW reward in case of a voting for the winning decision
  644. // or the losing of xDai and KNW in case of a voting for the losing decision
  645. // The first caller who executes this will also trigger the calculation whether the vote passed or not
  646. func Finalize(_proposalID string) (bool, bool, error) {
  647. if !config.DitConfig.PassedKYC {
  648. passedKYC, err := CheckForKYC()
  649. if err != nil {
  650. return false, false, err
  651. } else if !passedKYC {
  652. return false, false, errors.New("You didn't pass the KYC yet")
  653. }
  654. }
  655. // Converting the stdin string input of the user into an int
  656. proposalID, _ := strconv.Atoi(_proposalID)
  657. // Searching for this repositories object in the config
  658. repoIndex, err := searchForRepoInConfig()
  659. if err != nil {
  660. return false, false, err
  661. }
  662. var repositoryArray []config.Repository
  663. if config.DitConfig.DemoModeActive {
  664. repositoryArray = config.DitConfig.DemoRepositories
  665. } else {
  666. repositoryArray = config.DitConfig.LiveRepositories
  667. }
  668. repoHash := GetHashOfString(repositoryArray[repoIndex].Name)
  669. connection, err := getConnection()
  670. if err != nil {
  671. return false, false, err
  672. }
  673. // Convertig the hex-string-formatted address into an address object
  674. myAddress := common.HexToAddress(config.DitConfig.EthereumKeys.Address)
  675. // Create a new instance of the ditContract to access it
  676. ditCooordinatorInstance, err := getDitCoordinatorInstance(connection)
  677. if err != nil {
  678. return false, false, err
  679. }
  680. // Create a new instance of the KNWToken contract to access it
  681. KNWTokenInstance, err := getKNWTokenInstance(connection)
  682. if err != nil {
  683. return false, false, err
  684. }
  685. // Create a new instance of the KNWVoting contract to access it
  686. KNWVotingInstance, err := getKNWVotingInstance(connection)
  687. if err != nil {
  688. return false, false, err
  689. }
  690. // Searching for the corresponding vote in the votes stored in the config
  691. var voteIndex int
  692. for i := range repositoryArray[repoIndex].ActiveVotes {
  693. if repositoryArray[repoIndex].ActiveVotes[i].ID == proposalID {
  694. voteIndex = i
  695. break
  696. }
  697. }
  698. // If this user already called the Resolve function for this vote it's not possible anymore
  699. if repositoryArray[repoIndex].ActiveVotes[voteIndex].Resolved {
  700. return false, false, errors.New("You already finalized this vote")
  701. }
  702. // Verifying whether the vote has already ended
  703. pollEnded, err := KNWVotingInstance.PollEnded(nil, big.NewInt(int64(repositoryArray[repoIndex].ActiveVotes[voteIndex].KNWVoteID)))
  704. if err != nil {
  705. return false, false, errors.New("Failed to retrieve vote status")
  706. }
  707. // If not, we can't resolve it
  708. if !pollEnded {
  709. return false, false, errors.New("The vote hasn't ended yet")
  710. }
  711. // Verifying whether the user is a participant of this vote
  712. didCommit, err := KNWVotingInstance.DidCommit(nil, myAddress, big.NewInt(int64(repositoryArray[repoIndex].ActiveVotes[voteIndex].KNWVoteID)))
  713. if err != nil {
  714. return false, false, errors.New("Failed to retrieve commit status")
  715. }
  716. // Retrieve the selected proposal obkect
  717. proposal, err := ditCooordinatorInstance.ProposalsOfRepository(nil, repoHash, big.NewInt(int64(proposalID)))
  718. if err != nil {
  719. return false, false, errors.New("Failed to retrieve the new proposal")
  720. }
  721. // Indicates whether the called was the proposer or not
  722. isProposer := (proposal.Proposer == myAddress)
  723. // If not, we are not allowed to call this function (it would fail)
  724. if !didCommit && myAddress != proposal.Proposer {
  725. return false, false, errors.New("You didn't participate in this vote")
  726. }
  727. // Saving the old xDai balance
  728. oldxDaiBalance, err := connection.BalanceAt(context.Background(), myAddress, nil)
  729. if err != nil {
  730. return false, false, errors.New("Failed to retrieve " + config.DitConfig.Currency + " balance")
  731. }
  732. // Saving the old KNW balance
  733. oldKNWBalance, err := KNWTokenInstance.BalanceOfLabel(nil, myAddress, repositoryArray[repoIndex].ActiveVotes[voteIndex].KnowledgeLabel)
  734. if err != nil {
  735. return false, false, errors.New("Failed to retrieve KNW balance")
  736. }
  737. // Crerating the transaction (basic values)
  738. auth, err := populateTx(connection)
  739. if err != nil {
  740. return false, false, err
  741. }
  742. // Resolving the vote
  743. transaction, err := ditCooordinatorInstance.FinalizeVote(auth, repoHash, big.NewInt(int64(proposalID)))
  744. if err != nil {
  745. if strings.Contains(err.Error(), "insufficient funds") {
  746. return false, false, errors.New("Your account doesn't have enough xDai to pay for the transaction")
  747. }
  748. return false, false, errors.New("Failed to finalize the vote: " + err.Error())
  749. }
  750. // Waiting for the resolve transaction to be mined
  751. helpers.Printf("Waiting for finalizing transaction to be mined", 0)
  752. waitingFor := 0
  753. newxDaiBalance := oldxDaiBalance
  754. for oldxDaiBalance.Cmp(newxDaiBalance) == 0 {
  755. waitingFor += 5
  756. time.Sleep(5 * time.Second)
  757. fmt.Printf(".")
  758. // Checking the balance of the user every 5 seconds, if it changed, a transaction was executed
  759. newxDaiBalance, err = connection.BalanceAt(context.Background(), myAddress, nil)
  760. if err != nil {
  761. return false, false, errors.New("Failed to retrieve opening status")
  762. }
  763. // If we are waiting for more than 2 minutes, the transaction might have failed
  764. if waitingFor > 180 {
  765. fmt.Printf("\n")
  766. helpers.PrintLine("Waiting for over 3 minutes, maybe the transaction or the network failed?", 1)
  767. helpers.PrintLine("Check at: https://blockscout.com/poa/dai/tx/"+transaction.Hash().Hex(), 1)
  768. os.Exit(0)
  769. }
  770. }
  771. fmt.Printf("\n")
  772. // Saving the new KNW balance after resolving the vote
  773. newKNWBalance, err := KNWTokenInstance.BalanceOfLabel(nil, myAddress, repositoryArray[repoIndex].ActiveVotes[voteIndex].KnowledgeLabel)
  774. if err != nil {
  775. return false, false, errors.New("Failed to retrieve KNW balance")
  776. }
  777. // Retrieving the outcome of the vote
  778. pollPassed, err := KNWVotingInstance.IsPassed(nil, big.NewInt(int64(repositoryArray[repoIndex].ActiveVotes[voteIndex].KNWVoteID)))
  779. if err != nil {
  780. return false, false, errors.New("Failed to retrieve vote outcome")
  781. }
  782. fmt.Printf("\n")
  783. // Show the user how the vote ended
  784. if pollPassed {
  785. helpers.PrintLine("Successfully finalized the vote - it passed", 0)
  786. if proposal.Proposer == common.HexToAddress(config.DitConfig.EthereumKeys.Address) {
  787. helpers.PrintLine("You received your stake back and will share the opposing voters slashes stakes", 0)
  788. }
  789. } else {
  790. helpers.PrintLine("Successfully finalized the vote - it didn't pass", 0)
  791. if proposal.Proposer == common.HexToAddress(config.DitConfig.EthereumKeys.Address) {
  792. helpers.PrintLine("Your stake was slashed and will be distributed amongst the voters", 0)
  793. }
  794. }
  795. // If the user got some xDai as a reward, this will be shown to the user
  796. if newxDaiBalance.Cmp(oldxDaiBalance) > 0 {
  797. difference := newxDaiBalance.Sub(newxDaiBalance, oldxDaiBalance)
  798. floatDifference := new(big.Float).Quo((new(big.Float).SetInt64(difference.Int64())), big.NewFloat(1000000000000000000))
  799. helpers.PrintLine(fmt.Sprintf("You gained %.2f "+config.DitConfig.Currency+"\n", floatDifference), 0)
  800. }
  801. // Also shwoing the user how man KNW tokens he got/lost
  802. if oldKNWBalance.Cmp(newKNWBalance) < 0 {
  803. difference := newKNWBalance.Sub(newKNWBalance, oldKNWBalance)
  804. floatDifference := new(big.Float).Quo((new(big.Float).SetInt64(difference.Int64())), big.NewFloat(1000000000000000000))
  805. helpers.PrintLine(fmt.Sprintf("You earned %f KNW Tokens for the knowledge label '%s'\n", floatDifference, repositoryArray[repoIndex].ActiveVotes[voteIndex].KnowledgeLabel), 0)
  806. } else if oldKNWBalance.Cmp(newKNWBalance) > 0 {
  807. difference := oldKNWBalance.Sub(oldKNWBalance, newKNWBalance)
  808. floatDifference := new(big.Float).Quo((new(big.Float).SetInt64(difference.Int64())), big.NewFloat(1000000000000000000))
  809. helpers.PrintLine(fmt.Sprintf("You lost %f KNW Tokens for the knowledge label '%s'\n", floatDifference, repositoryArray[repoIndex].ActiveVotes[voteIndex].KnowledgeLabel), 0)
  810. }
  811. // Saving the resolved-status in the config
  812. repositoryArray[repoIndex].ActiveVotes[voteIndex].Resolved = true
  813. if config.DitConfig.DemoModeActive {
  814. config.DitConfig.DemoRepositories = repositoryArray
  815. } else {
  816. config.DitConfig.LiveRepositories = repositoryArray
  817. }
  818. // Saving the config back to the file
  819. err = config.Save()
  820. if err != nil {
  821. return false, false, err
  822. }
  823. // if config.DitConfig.DemoModeActive && len(config.DitConfig.DemoRepositories[repoIndex].ActiveVotes[voteIndex].DemoChoices) == 3 {
  824. // for i := 0; i < 3; i++ {
  825. // _, err := demo.Finalize(_proposalID, i)
  826. // if err != nil {
  827. // helpers.PrintLine("Error during vote finalizing of demo voter "+strconv.Itoa(i), 2)
  828. // }
  829. // }
  830. // }
  831. return pollPassed, isProposer, nil
  832. }
  833. // GetVoteInfo will print information about a vote
  834. func GetVoteInfo(_proposalID ...int) error {
  835. // Searching for this repositories object in the config
  836. repoIndex, err := searchForRepoInConfig()
  837. if err != nil {
  838. return err
  839. }
  840. var repositoryArray []config.Repository
  841. if config.DitConfig.DemoModeActive {
  842. repositoryArray = config.DitConfig.DemoRepositories
  843. } else {
  844. repositoryArray = config.DitConfig.LiveRepositories
  845. }
  846. repoHash := GetHashOfString(repositoryArray[repoIndex].Name)
  847. connection, err := getConnection()
  848. if err != nil {
  849. return err
  850. }
  851. // Create a new instance of the ditContract to access it
  852. ditCooordinatorInstance, err := getDitCoordinatorInstance(connection)
  853. if err != nil {
  854. return err
  855. }
  856. // Create a new instance of the KNWVoting contract to access it
  857. KNWVotingInstance, err := getKNWVotingInstance(connection)
  858. if err != nil {
  859. return err
  860. }
  861. // Retrieving the current proposalID
  862. currentProposalIDBigInt, err := ditCooordinatorInstance.GetCurrentProposalID(nil, repoHash)
  863. if err != nil {
  864. return errors.New("Failed to retrieve the current proposal id")
  865. }
  866. // Converting the current proposal id of the ditContract to an int
  867. // if it is zero, there are no votes
  868. currentProposalID := int(currentProposalIDBigInt.Int64())
  869. if currentProposalID == 0 {
  870. helpers.PrintLine("There are no votes for this repository", 1)
  871. os.Exit(0)
  872. }
  873. // If a proposalID is specified in the function call, use that
  874. var proposalID int
  875. if len(_proposalID) > 0 {
  876. proposalID = _proposalID[0]
  877. // If not, let the user select on that is between 1 and the current proposal ID
  878. } else {
  879. for proposalID < 1 || proposalID > currentProposalID {
  880. proposalID, _ = strconv.Atoi(helpers.GetUserInput("Select a proposal ID between 1 and " + strconv.Itoa(currentProposalID)))
  881. }
  882. }
  883. // Retrieve the selected proposal obkect
  884. proposal, err := ditCooordinatorInstance.ProposalsOfRepository(nil, repoHash, big.NewInt(int64(proposalID)))
  885. if err != nil {
  886. return errors.New("Failed to retrieve the new proposal")
  887. }
  888. // Retrieving information about this vote from the KNWVoting contract itself
  889. KNWPoll, err := KNWVotingInstance.PollMap(nil, proposal.KNWVoteID)
  890. if err != nil {
  891. return errors.New("Failed to retrieve vote information")
  892. }
  893. // Retrieving grossStake from this vote
  894. grossStake, err := KNWVotingInstance.GetGrossStake(nil, proposal.KNWVoteID)
  895. if err != nil {
  896. return errors.New("Failed to retrieve the gross stake")
  897. }
  898. floatGrossStake := new(big.Float).Quo((new(big.Float).SetInt(grossStake)), big.NewFloat(1000000000000000000))
  899. // Retrieving netStake from this vote
  900. netStake, err := KNWVotingInstance.GetNetStake(nil, proposal.KNWVoteID)
  901. if err != nil {
  902. return errors.New("Failed to retrieve the net stake")
  903. }
  904. floatNetStake := new(big.Float).Quo((new(big.Float).SetInt(netStake)), big.NewFloat(1000000000000000000))
  905. // Formatting the commit and reveal time to a human-readable format
  906. timeCommit := time.Unix(KNWPoll.CommitEndDate.Int64(), 0)
  907. timeCommitString := timeCommit.Format("15:04:05 on 2006/01/02")
  908. timeReveal := time.Unix(KNWPoll.RevealEndDate.Int64(), 0)
  909. timeRevealString := timeReveal.Format("15:04:05 on 2006/01/02")
  910. // Printing the information about this vote
  911. helpers.PrintLine("---------------------------", 0)
  912. helpers.PrintLine("Proposal ID: "+strconv.Itoa(proposalID), 0)
  913. helpers.PrintLine("URL: https://github.com/"+repositoryArray[repoIndex].Name+"/tree/dit_proposal_"+strconv.Itoa(proposalID), 0)
  914. helpers.PrintLine("Proposer: "+proposal.Proposer.Hex(), 0)
  915. helpers.PrintLine("Knowledge-Label: "+proposal.KnowledgeLabel, 0)
  916. // If the user participated in this vote, the choice and stake/KNW are also printed
  917. for i := range repositoryArray[repoIndex].ActiveVotes {
  918. if repositoryArray[repoIndex].ActiveVotes[i].ID == proposalID {
  919. floatxDai := new(big.Float).Quo((new(big.Float).SetInt(big.NewInt(int64(repositoryArray[repoIndex].ActiveVotes[i].NumTokens)))), big.NewFloat(1000000000000000000))
  920. floatKNW := new(big.Float).Quo((new(big.Float).SetInt(big.NewInt(int64(repositoryArray[repoIndex].ActiveVotes[i].NumKNW)))), big.NewFloat(1000000000000000000))
  921. if proposal.Proposer.Hex() != config.DitConfig.EthereumKeys.Address {
  922. helpers.PrintLine("Your choice: "+strconv.Itoa(repositoryArray[repoIndex].ActiveVotes[i].Choice), 0)
  923. }
  924. helpers.PrintLine(fmt.Sprintf("You staked %.2f %s and used %f KNW\n", floatxDai, config.DitConfig.Currency, floatKNW), 0)
  925. break
  926. } else {
  927. helpers.PrintLine(fmt.Sprintf("Required (gross) stake: %.2f "+config.DitConfig.Currency+"\n", floatGrossStake), 0)
  928. }
  929. }
  930. helpers.PrintLine(fmt.Sprintf("Current net stake: %.2f "+config.DitConfig.Currency+"\n", floatNetStake), 0)
  931. helpers.PrintLine("Vote phase end: "+timeCommitString, 0)
  932. helpers.PrintLine("Opening phase end: "+timeRevealString, 0)
  933. helpers.PrintLine("Resolved? "+strconv.FormatBool(proposal.IsFinalized), 0)
  934. helpers.PrintLine("Passed? "+strconv.FormatBool(proposal.ProposalAccepted), 0)
  935. helpers.PrintLine("---------------------------", 0)
  936. return nil
  937. }
  938. // GetBalances will print the xDai and KNW balances
  939. func GetBalances() error {
  940. connection, err := getConnection()
  941. if err != nil {
  942. return err
  943. }
  944. if len(config.DitConfig.KNWToken) != correctETHAddressLength {
  945. return errors.New("Invalid KNWToken address, please do '" + helpers.ColorizeCommand("set_coordinator") + "' first")
  946. }
  947. // Convertig the hex-string-formatted address into an address object
  948. myAddress := common.HexToAddress(config.DitConfig.EthereumKeys.Address)
  949. // Create a new instance of the KNWToken to access it
  950. KNWTokenInstance, err := getKNWTokenInstance(connection)
  951. if err != nil {
  952. return err
  953. }
  954. // Retrieving the number of knowledge-labels where the user has a balance in
  955. labelCount, err := KNWTokenInstance.LabelCountOfAddress(nil, myAddress)
  956. if err != nil {
  957. return errors.New("Failed to retrieve knowledge label count of address")
  958. }
  959. // Converting this count to an int
  960. labelCountInt := labelCount.Int64()
  961. // Retrieving each of the labels
  962. var labelsOfAddress []string
  963. for i := 1; i <= int(labelCountInt); i++ {
  964. newLabel, err := KNWTokenInstance.LabelOfAddress(nil, myAddress, big.NewInt(int64(i)))
  965. if err != nil {
  966. return errors.New("Failed to retrieve knowledge label count of address")
  967. }
  968. if len(newLabel) > 0 {
  969. labelsOfAddress = append(labelsOfAddress, newLabel)
  970. }
  971. }
  972. var floatBalance *big.Float
  973. if !config.DitConfig.DemoModeActive {
  974. // Retrieving the xDai balance of the user
  975. xDaiBalance, err := connection.BalanceAt(context.Background(), myAddress, nil)
  976. if err != nil {
  977. return errors.New("Failed to retrieve " + config.DitConfig.Currency + " balance")
  978. }
  979. helpers.PrintLine("Balances for address "+config.DitConfig.EthereumKeys.Address+":", 0)
  980. // Formatting the xDai balance to a human-readable format
  981. floatBalance = new(big.Float).Quo((new(big.Float).SetInt(xDaiBalance)), big.NewFloat(1000000000000000000))
  982. } else {
  983. // Create a new instance of the ditToken to access it
  984. ditTokenInstance, err := getDitTokenInstance(connection)
  985. if err != nil {
  986. return err
  987. }
  988. // Retrieving the xDit balance of the user
  989. xDitBalance, err := ditTokenInstance.BalanceOf(nil, myAddress)
  990. if err != nil {
  991. return errors.New("Failed to retrieve " + config.DitConfig.Currency + " balance")
  992. }
  993. helpers.PrintLine("Balances for address "+config.DitConfig.EthereumKeys.Address+":", 0)
  994. // Formatting the xDai balance to a human-readable format
  995. floatBalance = new(big.Float).Quo((new(big.Float).SetInt(xDitBalance)), big.NewFloat(1000000000000000000))
  996. }
  997. helpers.PrintLine(fmt.Sprintf(config.DitConfig.Currency+"-Balance: %.2f "+config.DitConfig.Currency, floatBalance), 0)
  998. helpers.PrintLine("", 0)
  999. // Printing each KNW balance
  1000. helpers.PrintLine("KNW-Balances:", 0)
  1001. if len(labelsOfAddress) == 0 {
  1002. helpers.PrintLine("- No labels with any balance found", 0)
  1003. } else {
  1004. for i := 0; i < len(labelsOfAddress); i++ {
  1005. // Retrieve each KNW balance
  1006. balanceOfLabel, err := KNWTokenInstance.BalanceOfLabel(nil, myAddress, labelsOfAddress[i])
  1007. if err != nil {
  1008. return err
  1009. }
  1010. // Formatting each KNW balance to a human-readable format
  1011. floatBalance := new(big.Float).Quo((new(big.Float).SetInt(balanceOfLabel)), big.NewFloat(1000000000000000000))
  1012. helpers.PrintLine(fmt.Sprintf(" - "+labelsOfAddress[i]+": %.2f KNW", floatBalance), 0)
  1013. }
  1014. }
  1015. return nil
  1016. }
  1017. // CheckForKYC will return whether a user has already passed the KYC
  1018. func CheckForKYC() (bool, error) {
  1019. connection, err := getConnection()
  1020. if err != nil {
  1021. return false, err
  1022. }
  1023. // Convertig the hex-string-formatted address into an address object
  1024. myAddress := common.HexToAddress(config.DitConfig.EthereumKeys.Address)
  1025. // Create a new instance of the KNWToken to access it
  1026. ditCoordinatorInstance, err := getDitCoordinatorInstance(connection)
  1027. if err != nil {
  1028. return false, err
  1029. }
  1030. passedKYC, err := ditCoordinatorInstance.PassedKYC(nil, myAddress)
  1031. if err != nil {
  1032. return false, err
  1033. }
  1034. if !passedKYC {
  1035. return false, nil
  1036. }
  1037. config.DitConfig.PassedKYC = true
  1038. err = config.Save()
  1039. if err != nil {
  1040. return false, err
  1041. }
  1042. return true, nil
  1043. }
  1044. // gatherProposalInfo will retrieve necessary information about a proposal from the ditContract
  1045. func gatherProposalInfo(_connection *ethclient.Client, _ditCoordinatorInstance *ditCoordinator.DitCoordinator, _repoHash [32]byte, _proposalID int64) (config.ActiveVote, error) {
  1046. // Retrieve the proposal object from the ditContract
  1047. var newVote config.ActiveVote
  1048. proposal, err := _ditCoordinatorInstance.ProposalsOfRepository(nil, _repoHash, big.NewInt(_proposalID))
  1049. if err != nil {
  1050. return newVote, errors.New("Failed to retrieve the new proposal")
  1051. }
  1052. // Store the information about this vote in an object
  1053. newVote.ID = int(_proposalID)
  1054. newVote.KNWVoteID = int(proposal.KNWVoteID.Int64())
  1055. newVote.KnowledgeLabel = proposal.KnowledgeLabel
  1056. // Create a new instance of the KNWVoting contract to access it
  1057. KNWVotingInstance, err := getKNWVotingInstance(_connection)
  1058. if err != nil {
  1059. return newVote, err
  1060. }
  1061. // Retrieving the information about this vote from the KNWVOting contract itself
  1062. KNWPoll, err := KNWVotingInstance.PollMap(nil, big.NewInt(int64(newVote.KNWVoteID)))
  1063. if err != nil {
  1064. return newVote, errors.New("Failed to retrieve vote information")
  1065. }
  1066. ethereumAddress := config.DitConfig.EthereumKeys.Address
  1067. newVote.CommitEnd = int(KNWPoll.CommitEndDate.Int64())
  1068. newVote.RevealEnd = int(KNWPoll.RevealEndDate.Int64())
  1069. // Retrieving the amount of xDai a user has staked for this vote
  1070. numTokens, err := KNWVotingInstance.GetGrossStake(nil, big.NewInt(int64(newVote.KNWVoteID)))
  1071. if err != nil {
  1072. return newVote, errors.New("Failed to retrieve grossStake")
  1073. }
  1074. // Retrieving the number of votes a user has for this vote
  1075. numVotes, err := KNWVotingInstance.GetNumVotes(nil, common.HexToAddress(ethereumAddress), big.NewInt(int64(newVote.KNWVoteID)))
  1076. if err != nil {
  1077. return newVote, errors.New("Failed to retrieve numVotes")
  1078. }
  1079. // Retrieving the number of KNW tokens a user has staked for this vote
  1080. numKNW, err := KNWVotingInstance.GetNumKNW(nil, common.HexToAddress(ethereumAddress), big.NewInt(int64(newVote.KNWVoteID)))
  1081. if err != nil {
  1082. return newVote, errors.New("Failed to retrieve numKNW")
  1083. }
  1084. newVote.NumTokens = int(numTokens.Int64())
  1085. newVote.NumVotes = int(numVotes.Int64())
  1086. newVote.NumKNW = int(numKNW.Int64())
  1087. return newVote, nil
  1088. }
  1089. // TODO
  1090. func initDitRepository(_ditCoordinatorInstance *ditCoordinator.DitCoordinator, _repoHash [32]byte) error {
  1091. connection, err := getConnection()
  1092. if err != nil {
  1093. return err
  1094. }
  1095. // Setting the voting setting for this repository
  1096. var voteSettings [7]*big.Int
  1097. // Majority (in percent)
  1098. voteSettings[0] = big.NewInt(50)
  1099. // KNW minting method (0 = regular)
  1100. voteSettings[1] = big.NewInt(0)
  1101. // KNW burning method (0 = square-root based, 1 = divide by 2 each time, 2 = proportial to the winning percentage)
  1102. voteSettings[2] = big.NewInt(0)
  1103. // TODO
  1104. voteSettings[3] = big.NewInt(120)
  1105. // TODO
  1106. voteSettings[4] = big.NewInt(86400)
  1107. // TODO
  1108. voteSettings[5] = big.NewInt(120)
  1109. // TODO
  1110. voteSettings[6] = big.NewInt(86400)
  1111. // Prompting the user to provide 1 to 3 knowledge-labels for this repository
  1112. helpers.PrintLine("Please provide knowledge labels that will be used for this repository:", 0)
  1113. helpers.PrintLine("Note: At least one has to be provided, press ENTER to skip", 0)
  1114. var knowledgeLabels []string
  1115. for len(knowledgeLabels) < 3 {
  1116. newLabel := helpers.GetUserInput("Knowledge Label " + strconv.Itoa(len(knowledgeLabels)+1))
  1117. if len(newLabel) > 0 {
  1118. knowledgeLabels = append(knowledgeLabels, newLabel)
  1119. }
  1120. if len(newLabel) == 0 && len(knowledgeLabels) > 0 {
  1121. if len(knowledgeLabels) < 3 {
  1122. for i := 0; i <= 3-len(knowledgeLabels); i++ {
  1123. knowledgeLabels = append(knowledgeLabels, "")
  1124. }
  1125. }
  1126. break
  1127. }
  1128. }
  1129. // Crerating the transaction (basic values)
  1130. auth, err := populateTx(connection)
  1131. if err != nil {
  1132. return err
  1133. }
  1134. // Initializing the repository = deploying a new ditContract
  1135. transaction, err := _ditCoordinatorInstance.InitRepository(auth, _repoHash, knowledgeLabels[0], knowledgeLabels[1], knowledgeLabels[2], voteSettings)
  1136. if err != nil {
  1137. if strings.Contains(err.Error(), "insufficient funds") {
  1138. return errors.New("Your account doesn't have enough xDai to pay for the transaction")
  1139. }
  1140. return err
  1141. }
  1142. // Waiting for the deployment transaction to be mined
  1143. helpers.Printf("Waiting for initialization transaction to be mined", 0)
  1144. boolTrue := true
  1145. isInitialized := false
  1146. waitingFor := 0
  1147. for isInitialized != boolTrue {
  1148. waitingFor += 5
  1149. time.Sleep(5 * time.Second)
  1150. fmt.Printf(".")
  1151. // Checking the repository status every 5 seconds
  1152. isInitialized, err = _ditCoordinatorInstance.RepositoryIsInitialized(nil, _repoHash)
  1153. if err != nil {
  1154. return err
  1155. }
  1156. // If we are waiting for more than 2 minutes, the transaction might have failed
  1157. if waitingFor > 180 {
  1158. fmt.Printf("\n")
  1159. helpers.PrintLine("Waiting for over 3 minutes, maybe the transaction or the network failed?", 1)
  1160. helpers.PrintLine("Check at: https://blockscout.com/poa/dai/tx/"+transaction.Hash().Hex(), 1)
  1161. os.Exit(0)
  1162. }
  1163. }
  1164. fmt.Printf("\n")
  1165. return nil
  1166. }
  1167. // populateTX will set the necessary values for a ethereum transaction
  1168. // amount of gas, gasprice, nonce, sign this with the private key
  1169. func populateTx(_connection *ethclient.Client) (*bind.TransactOpts, error) {
  1170. // Retrieve the decrypted private key through a password prompt
  1171. var err error
  1172. privateKeyString, err := config.GetPrivateKey()
  1173. if err != nil {
  1174. return nil, err
  1175. }
  1176. // Converting the private key string into a private key object
  1177. privateKey, err := crypto.HexToECDSA(privateKeyString)
  1178. if err != nil {
  1179. return nil, errors.New("Failed to convert ethereum private-key")
  1180. }
  1181. // Retrieving the current pending nonce of our address
  1182. pendingNonce, err := _connection.PendingNonceAt(context.Background(), common.HexToAddress(config.DitConfig.EthereumKeys.Address))
  1183. if err != nil {
  1184. return nil, errors.New("Failed to retrieve nonce for ethereum transaction")
  1185. }
  1186. // Retrieving the current non-pending nonce of our address
  1187. nonpendingNonce, err := _connection.NonceAt(context.Background(), common.HexToAddress(config.DitConfig.EthereumKeys.Address), nil)
  1188. if err != nil {
  1189. return nil, errors.New("Failed to retrieve nonce for ethereum transaction")
  1190. }
  1191. // Edge-Case for slow nodes
  1192. nonce := pendingNonce
  1193. if nonpendingNonce > pendingNonce {
  1194. nonce = nonpendingNonce
  1195. }
  1196. // Retrieving the suggested gasprice by the network
  1197. gasPrice, err := _connection.SuggestGasPrice(context.Background())
  1198. if err != nil {
  1199. return nil, errors.New("Failed to retrieve the gas-price for ethereum transaction")
  1200. }
  1201. // Minimum gas price is 10 gwei for now, which works best for rinkeby
  1202. // Will be changed later on
  1203. defaultGasPrice := big.NewInt(1000000000)
  1204. if gasPrice.Cmp(defaultGasPrice) != 1 {
  1205. gasPrice = defaultGasPrice
  1206. }
  1207. // Setting the values into the transaction-options object
  1208. auth := bind.NewKeyedTransactor(privateKey)
  1209. auth.Nonce = big.NewInt(int64(nonce))
  1210. auth.Value = big.NewInt(0)
  1211. auth.GasLimit = uint64(1000000)
  1212. auth.GasPrice = gasPrice
  1213. return auth, nil
  1214. }
  1215. func getDitCoordinatorInstance(_connection *ethclient.Client, _ditCoordinatorAddress ...string) (*ditCoordinator.DitCoordinator, error) {
  1216. var ditCoordinatorAddressString string
  1217. if len(_ditCoordinatorAddress) > 0 {
  1218. ditCoordinatorAddressString = _ditCoordinatorAddress[0]
  1219. } else {
  1220. if len(config.DitConfig.DitCoordinator) != correctETHAddressLength {
  1221. return nil, errors.New("Invalid ditCoordinator address, please do '" + helpers.ColorizeCommand("set_coordinator") + "' first")
  1222. }
  1223. ditCoordinatorAddressString = config.DitConfig.DitCoordinator
  1224. }
  1225. // Convertig the hex-string-formatted address into an address object
  1226. ditCoordinatorAddress := common.HexToAddress(ditCoordinatorAddressString)
  1227. // Create a new instance of the ditCoordinator to access it
  1228. ditCoordinatorInstance, err := ditCoordinator.NewDitCoordinator(ditCoordinatorAddress, _connection)
  1229. if err != nil {
  1230. return nil, errors.New("Failed to find ditCoordinator at provided address")
  1231. }
  1232. return ditCoordinatorInstance, nil
  1233. }
  1234. func getDitDemoCoordinatorInstance(_connection *ethclient.Client, _ditDemoCoordinatorAddress ...string) (*ditDemoCoordinator.DitDemoCoordinator, error) {
  1235. var ditDemoCoordinatorAddressString string
  1236. if len(_ditDemoCoordinatorAddress) > 0 {
  1237. ditDemoCoordinatorAddressString = _ditDemoCoordinatorAddress[0]
  1238. } else {
  1239. if len(config.DitConfig.DitCoordinator) != correctETHAddressLength {
  1240. return nil, errors.New("Invalid ditDemoCoordinator address, please do '" + helpers.ColorizeCommand("set_coordinator") + "' first")
  1241. }
  1242. ditDemoCoordinatorAddressString = config.DitConfig.DitCoordinator
  1243. }
  1244. // Convertig the hex-string-formatted address into an address object
  1245. ditDemoCoordinatorAddress := common.HexToAddress(ditDemoCoordinatorAddressString)
  1246. // Create a new instance of the ditDemoCoordinator to access it
  1247. ditDemoCoordinatorInstance, err := ditDemoCoordinator.NewDitDemoCoordinator(ditDemoCoordinatorAddress, _connection)
  1248. if err != nil {
  1249. return nil, errors.New("Failed to find ditDemoCoordinator at provided address")
  1250. }
  1251. return ditDemoCoordinatorInstance, nil
  1252. }
  1253. func getKNWTokenInstance(_connection *ethclient.Client) (*KNWToken.KNWToken, error) {
  1254. KNWTokenAddress := common.HexToAddress(config.DitConfig.KNWToken)
  1255. // Create a new instance of the KNWToken contract to access it
  1256. KNWTokenInstance, err := KNWToken.NewKNWToken(KNWTokenAddress, _connection)
  1257. if err != nil {
  1258. return nil, errors.New("Failed to find KNWToken at provided address")
  1259. }
  1260. return KNWTokenInstance, nil
  1261. }
  1262. func getDitTokenInstance(_connection *ethclient.Client) (*ditToken.MintableERC20, error) {
  1263. ditTokenAddress := common.HexToAddress(config.DitConfig.DitToken)
  1264. // Create a new instance of the KNWToken contract to access it
  1265. ditTokenInstance, err := ditToken.NewMintableERC20(ditTokenAddress, _connection)
  1266. if err != nil {
  1267. return nil, errors.New("Failed to find ditToken at provided address")
  1268. }
  1269. return ditTokenInstance, nil
  1270. }
  1271. func getKNWVotingInstance(_connection *ethclient.Client) (*KNWVoting.KNWVoting, error) {
  1272. KNWVotingAddress := common.HexToAddress(config.DitConfig.KNWVoting)
  1273. // Create a new instance of the KNWVoting contract to access it
  1274. KNWVotingInstance, err := KNWVoting.NewKNWVoting(KNWVotingAddress, _connection)
  1275. if err != nil {
  1276. return nil, errors.New("Failed to find KNWVoting at provided address")
  1277. }
  1278. return KNWVotingInstance, nil
  1279. }
  1280. // getConnection will return a connection to the ethereum blockchain
  1281. func getConnection() (*ethclient.Client, error) {
  1282. // Connecting to rinkeby via infura
  1283. connection, err := ethclient.Dial("https://sokol.poa.network")
  1284. if err != nil {
  1285. return nil, errors.New("Failed to connect to the ethereum network")
  1286. }
  1287. return connection, nil
  1288. }
  1289. // searchForRepoInConfig will search for the current repo in the config
  1290. func searchForRepoInConfig() (int64, error) {
  1291. // Retrieve the name of the repo we are in from git
  1292. repository, err := git.GetRepository()
  1293. if err != nil {
  1294. return 0, err
  1295. }
  1296. var repositoryArray []config.Repository
  1297. if config.DitConfig.DemoModeActive {
  1298. repositoryArray = config.DitConfig.DemoRepositories
  1299. } else {
  1300. repositoryArray = config.DitConfig.LiveRepositories
  1301. }
  1302. // Search for the repo in our config
  1303. for i := range repositoryArray {
  1304. if repositoryArray[i].Name == repository {
  1305. // Return the index of this repository if it was found
  1306. return int64(i), nil
  1307. }
  1308. }
  1309. // Return an error if nothing was found
  1310. return 0, errors.New("Repository hasn't been initialized")
  1311. }
  1312. // GetHashOfString takes a string a returns it as keccak256
  1313. func GetHashOfString(_string string) [32]byte {
  1314. repoHash32 := [32]byte{}
  1315. copy(repoHash32[:], crypto.Keccak256([]byte(_string))[:])
  1316. return repoHash32
  1317. }