Skip to content

Instantly share code, notes, and snippets.

@bladedoyle
Created November 17, 2020 19:55
Show Gist options
  • Save bladedoyle/a9b5f986cfaf61962a7b25545ee2a0a1 to your computer and use it in GitHub Desktop.
Save bladedoyle/a9b5f986cfaf61962a7b25545ee2a0a1 to your computer and use it in GitHub Desktop.
Grin Wallet Owner API V3 example in Python
#!/usr/bin/python3
# Start the node: $ grin
# Configure Wallet:
# owner_api_listen_port = 3420
# api_secret_path = "/home/bdoyle/.grin/main/.owner_api_secret"
# Start the wallet owner api: $ grin-wallet owner_api
# Install g++
# Install python3 and python3-pip
# pip3 install secp256k1
# pip3 install eciespy
# pip3 install coincurve
# https://pypi.org/project/eciespy/
# See the section "Secp256k1" about half way down the page
from coincurve import PrivateKey, PublicKey
import requests
import json
# set grin wallet basicauth credentials
wallet_user = "grin"
with open('/home/bdoyle/.grin/main/.owner_api_secret','r') as f:
wallet_secret = f.read()
# We need to call the v3 "init_secure_api" wallet owner api endpoint
# https://docs.rs/grin_wallet_api/4.0.0/grin_wallet_api/trait.OwnerRpc.html#tymethod.init_secure_api
my_key = PrivateKey.from_int(420)
my_public_key = my_key.public_key.format(False).hex() # Generate a public key, uncompressed, in hex
#print("my_public_key in hex: {}".format(my_public_key))
# Call init_secure_api
http_auth = requests.auth.HTTPBasicAuth(wallet_user, wallet_secret)
rpc_data = { "jsonrpc":"2.0",
"method":"init_secure_api",
"params":{"ecdh_pubkey": my_public_key},
"id":1
}
req = requests.post(
'http://localhost:3420/v3/owner',
json = rpc_data,
auth = http_auth,
)
req_j = req.json()
print("init_secure_api response: {}".format(req_j))
api_public_key_hex = req_j["result"]["Ok"]
# init_secure_api will return its public key used in the derivation,
# which the caller should multiply by its private key to derive the shared key.
# The shared key will be derived using ECDH with the provided public key on the secp256k1 curve.
api_public_key = PublicKey(bytes.fromhex(api_public_key_hex))
shared_ecdh_key = my_key.ecdh(api_public_key.format())
shared_ecdh_key_hex = shared_ecdh_key.hex()
print("shared_ecdh_key_hex: {}".format(shared_ecdh_key_hex))
## - I am very much not sure about what follows ...
# all further requests and responses are encrypted and decrypted
# See "AES" sectoion at the bottom of: https://github.com/ecies/py
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES, PKCS1_OAEP
import base64
rpc_data = '{ "jsonrpc": "2.0", "method": "accounts", "params": { "token": "d202964900000000d302964900000000d402964900000000d502964900000000" }, "id": 1 }'.encode("utf-8")
cipher_aes = AES.new(shared_ecdh_key, AES.MODE_GCM)
ciphertext, tag = cipher_aes.encrypt_and_digest(rpc_data)
v3_rpc_payload = {
"jsonrpc": "2.0",
"method": "encrypted_request_v3",
"id": "1",
"params": {
"nonce": cipher_aes.nonce,
"body_enc": base64.b64encode(ciphertext),
}
}
req = requests.post(
'http://localhost:3420/v3/owner',
json = v3_rpc_payload,
auth = http_auth,
)
print("accounts response: {}".format(req.text))
@sheldonth
Copy link

Any idea if this implementation is actually functional as-is ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment