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.

117 lines
3.8 KiB

10 years ago
10 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. func StartProcess(privKey acm.PrivKey, remote string, command btypes.CommandStartProcess) (response btypes.ResponseStartProcess, err error) {
  19. nonce, err := GetNonce(remote)
  20. if err != nil {
  21. return response, err
  22. }
  23. commandBytes, signature := SignCommand(privKey, nonce+1, command)
  24. _, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
  25. return response, err
  26. }
  27. func StopProcess(privKey acm.PrivKey, remote string, command btypes.CommandStopProcess) (response btypes.ResponseStopProcess, err error) {
  28. nonce, err := GetNonce(remote)
  29. if err != nil {
  30. return response, err
  31. }
  32. commandBytes, signature := SignCommand(privKey, nonce+1, command)
  33. _, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
  34. return response, err
  35. }
  36. func ListProcesses(privKey acm.PrivKey, remote string, command btypes.CommandListProcesses) (response btypes.ResponseListProcesses, err error) {
  37. nonce, err := GetNonce(remote)
  38. if err != nil {
  39. return response, err
  40. }
  41. commandBytes, signature := SignCommand(privKey, nonce+1, command)
  42. _, err = RunAuthCommand(remote, commandBytes, []acm.Signature{signature}, &response)
  43. return response, err
  44. }
  45. func DownloadFile(privKey acm.PrivKey, remote string, command btypes.CommandServeFile, outPath string) (n int64, err error) {
  46. // Create authCommandJSONBytes
  47. nonce, err := GetNonce(remote)
  48. if err != nil {
  49. return 0, err
  50. }
  51. commandBytes, signature := SignCommand(privKey, nonce+1, command)
  52. authCommand := btypes.AuthCommand{
  53. CommandJSONStr: string(commandBytes),
  54. Signatures: []acm.Signature{signature},
  55. }
  56. authCommandJSONBytes := binary.JSONBytes(authCommand)
  57. // Make request and write to outPath.
  58. httpResponse, err := http.PostForm(remote+"/download", url.Values{"auth_command": {string(authCommandJSONBytes)}})
  59. if err != nil {
  60. return 0, err
  61. }
  62. defer httpResponse.Body.Close()
  63. outFile, err := os.OpenFile(outPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
  64. if err != nil {
  65. return 0, err
  66. }
  67. defer outFile.Close()
  68. n, err = io.Copy(outFile, httpResponse.Body)
  69. if err != nil {
  70. return 0, err
  71. }
  72. return n, nil
  73. }
  74. //-----------------------------------------------------------------------------
  75. // Utility method to get nonce from the remote.
  76. // The next command should include the returned nonce+1 as nonce.
  77. func GetNonce(remote string) (uint64, error) {
  78. response, err := GetStatus(remote)
  79. return response.Nonce, err
  80. }
  81. func GetStatus(remote string) (response btypes.ResponseStatus, err error) {
  82. _, err = rpcclient.Call(remote, "status", Arr(), &response)
  83. if err != nil {
  84. return response, fmt.Errorf("Error fetching nonce from remote %v:\n %v", remote, err)
  85. }
  86. return response, nil
  87. }
  88. // Each developer runs this
  89. func SignCommand(privKey acm.PrivKey, nonce uint64, command btypes.Command) ([]byte, acm.Signature) {
  90. noncedCommand := btypes.NoncedCommand{
  91. Nonce: nonce,
  92. Command: command,
  93. }
  94. commandJSONBytes := binary.JSONBytes(noncedCommand)
  95. signature := privKey.Sign(commandJSONBytes)
  96. return commandJSONBytes, signature
  97. }
  98. // Somebody aggregates the signatures and calls this.
  99. func RunAuthCommand(remote string, commandJSONBytes []byte, signatures []acm.Signature, dest interface{}) (interface{}, error) {
  100. authCommand := btypes.AuthCommand{
  101. CommandJSONStr: string(commandJSONBytes),
  102. Signatures: signatures,
  103. }
  104. return rpcclient.Call(remote, "run", Arr(authCommand), dest)
  105. }