Gnosis Safe simple offline signer A simple tool to get signatures offline from a cold wallet.
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.

98 satır
2.5KB

  1. import sys, getopt
  2. import json
  3. from getpass import getpass
  4. from eth_account import Account
  5. from hexbytes import HexBytes
  6. """
  7. Main logic extracted from:
  8. https://github.com/gnosis/gnosis-py
  9. """
  10. def signature_to_bytes(v: int, r: int, s: int) -> bytes:
  11. """
  12. Convert ecdsa signature to bytes
  13. :param v:
  14. :param r:
  15. :param s:
  16. :return: signature in form of {bytes32 r}{bytes32 s}{uint8 v}
  17. """
  18. byte_order = "big"
  19. return (
  20. r.to_bytes(32, byteorder=byte_order)
  21. + s.to_bytes(32, byteorder=byte_order)
  22. + v.to_bytes(1, byteorder=byte_order)
  23. )
  24. def sign(private_key: str, safe_tx_hash: HexBytes) -> bytes:
  25. """
  26. {bytes32 r}{bytes32 s}{uint8 v}
  27. :param private_key:
  28. :return: Signature
  29. """
  30. account = Account.from_key(private_key)
  31. signature_dict = account.signHash(safe_tx_hash)
  32. signature = signature_to_bytes(
  33. signature_dict["v"], signature_dict["r"], signature_dict["s"]
  34. )
  35. """
  36. # Insert signature sorted
  37. if account.address not in self.signers:
  38. new_owners = self.signers + [account.address]
  39. new_owner_pos = sorted(new_owners, key=lambda x: int(x, 16)).index(
  40. account.address
  41. )
  42. self.signatures = (
  43. self.signatures[: 65 * new_owner_pos]
  44. + signature
  45. + self.signatures[65 * new_owner_pos :]
  46. )
  47. """
  48. return signature
  49. def get_private_key(keystore: str) -> str:
  50. with open(keystore) as f:
  51. encrypted = f.readlines()
  52. password = getpass("Enter password:")
  53. return Account.decrypt(json.loads(encrypted[0]), password)
  54. def trim_tx_hash(safe_tx_hash: str) -> str:
  55. if safe_tx_hash[0:2] == "0x":
  56. return safe_tx_hash[2:]
  57. return safe_tx_hash
  58. def get_args(argv):
  59. help_string = 'Usage: python3 safe_sign.py -s hash -k keystore'
  60. if len(argv) < 4:
  61. print(help_string)
  62. sys.exit(2)
  63. try:
  64. opts, args = getopt.getopt(argv,"hs:k:",["hash=,keystore="])
  65. except getopt.GetoptError:
  66. print(help_string)
  67. sys.exit(2)
  68. for opt, arg in opts:
  69. if opt == '-h':
  70. print(help_string)
  71. sys.exit()
  72. elif opt in ("-s", "--hash"):
  73. safe_tx_hash = arg
  74. elif opt in ("-k", "--keystore"):
  75. keystore = arg
  76. return safe_tx_hash, keystore
  77. if __name__ == "__main__":
  78. safe_tx_hash, keystore = get_args(sys.argv[1:])
  79. safe_tx_hash = trim_tx_hash(safe_tx_hash)
  80. pk = get_private_key(keystore)
  81. hb = HexBytes(safe_tx_hash)
  82. signature = sign(pk, hb)
  83. print(HexBytes(signature).hex())