You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

138 lines
4.7 KiB

9 years ago
9 years ago
  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "net/url"
  6. "os"
  7. acm "github.com/tendermint/tendermint/account"
  8. "github.com/tendermint/tendermint/binary"
  9. btypes "github.com/tendermint/tendermint/cmd/barak/types"
  10. . "github.com/tendermint/tendermint/common"
  11. "github.com/tendermint/tendermint/rpc/client"
  12. "net/http"
  13. )
  14. // These are convenience functions for a single developer.
  15. // When multiple are involved, the workflow is different.
  16. // (First the command(s) are signed by all validators,
  17. // and then it is broadcast).
  18. // TODO: Implement a reasonable workflow with multiple validators.
  19. func StartProcess(privKey acm.PrivKey, remote string, command btypes.CommandStartProcess) (response btypes.ResponseStartProcess, err error) {
  20. nonce, err := GetNonce(remote)
  21. if err != nil {
  22. return response, err
  23. }
  24. commandBytes, signature := SignCommand(privKey, nonce+1, command)
  25. _, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
  26. return response, err
  27. }
  28. func StopProcess(privKey acm.PrivKey, remote string, command btypes.CommandStopProcess) (response btypes.ResponseStopProcess, err error) {
  29. nonce, err := GetNonce(remote)
  30. if err != nil {
  31. return response, err
  32. }
  33. commandBytes, signature := SignCommand(privKey, nonce+1, command)
  34. _, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
  35. return response, err
  36. }
  37. func ListProcesses(privKey acm.PrivKey, remote string, command btypes.CommandListProcesses) (response btypes.ResponseListProcesses, err error) {
  38. nonce, err := GetNonce(remote)
  39. if err != nil {
  40. return response, err
  41. }
  42. commandBytes, signature := SignCommand(privKey, nonce+1, command)
  43. _, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
  44. return response, err
  45. }
  46. func OpenListener(privKey acm.PrivKey, remote string, command btypes.CommandOpenListener) (response btypes.ResponseOpenListener, err error) {
  47. nonce, err := GetNonce(remote)
  48. if err != nil {
  49. return response, err
  50. }
  51. commandBytes, signature := SignCommand(privKey, nonce+1, command)
  52. _, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
  53. return response, err
  54. }
  55. func CloseListener(privKey acm.PrivKey, remote string, command btypes.CommandCloseListener) (response btypes.ResponseCloseListener, err error) {
  56. nonce, err := GetNonce(remote)
  57. if err != nil {
  58. return response, err
  59. }
  60. commandBytes, signature := SignCommand(privKey, nonce+1, command)
  61. _, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
  62. return response, err
  63. }
  64. func DownloadFile(privKey acm.PrivKey, remote string, command btypes.CommandServeFile, outPath string) (n int64, err error) {
  65. // Create authCommandJSONBytes
  66. nonce, err := GetNonce(remote)
  67. if err != nil {
  68. return 0, err
  69. }
  70. commandBytes, signature := SignCommand(privKey, nonce+1, command)
  71. authCommand := btypes.AuthCommand{
  72. CommandJSONStr: string(commandBytes),
  73. Signatures: []acm.Signature{signature},
  74. }
  75. authCommandJSONBytes := binary.JSONBytes(authCommand)
  76. // Make request and write to outPath.
  77. httpResponse, err := http.PostForm(remote+"/download", url.Values{"auth_command": {string(authCommandJSONBytes)}})
  78. if err != nil {
  79. return 0, err
  80. }
  81. defer httpResponse.Body.Close()
  82. outFile, err := os.OpenFile(outPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
  83. if err != nil {
  84. return 0, err
  85. }
  86. defer outFile.Close()
  87. n, err = io.Copy(outFile, httpResponse.Body)
  88. if err != nil {
  89. return 0, err
  90. }
  91. return n, nil
  92. }
  93. //-----------------------------------------------------------------------------
  94. // Utility method to get nonce from the remote.
  95. // The next command should include the returned nonce+1 as nonce.
  96. func GetNonce(remote string) (int64, error) {
  97. response, err := GetStatus(remote)
  98. return response.Nonce, err
  99. }
  100. func GetStatus(remote string) (response btypes.ResponseStatus, err error) {
  101. _, err = rpcclient.Call(remote, "status", Arr(), &response)
  102. if err != nil {
  103. return response, fmt.Errorf("Error fetching nonce from remote %v:\n %v", remote, err)
  104. }
  105. return response, nil
  106. }
  107. // Each developer runs this
  108. func SignCommand(privKey acm.PrivKey, nonce int64, command btypes.Command) ([]byte, acm.Signature) {
  109. noncedCommand := btypes.NoncedCommand{
  110. Nonce: nonce,
  111. Command: command,
  112. }
  113. commandJSONBytes := binary.JSONBytes(noncedCommand)
  114. signature := privKey.Sign(commandJSONBytes)
  115. return commandJSONBytes, signature
  116. }
  117. // Somebody aggregates the signatures and calls this.
  118. func RunAuthCommand(remote string, commandJSONBytes []byte, signatures []acm.Signature, dest interface{}) (interface{}, error) {
  119. authCommand := btypes.AuthCommand{
  120. CommandJSONStr: string(commandJSONBytes),
  121. Signatures: signatures,
  122. }
  123. return rpcclient.Call(remote, "run", Arr(authCommand), dest)
  124. }