Recently I'm proceeding with the project "auto publisher" which publishes my articles from the repository to my blogs following its category. For example, If I have two blogs, A and B where A is for blockchain & IT and B is for daily life and there are 2 posts, one(a) is about blockchain and another(b) is about daily training in my repo, then my bot will send (a) to A blog and (b) to B blog.
I chose 4 blogs: Steem, Tistory, Naverblog, Wordpress and have coded with thier API (except naverblog). In this article I explained codes to get a image link from steemitimages.com (not from imgur or other sites). To request a image link to steemimages.com image and it's signature should be sent. The signature is created using prefix word(ImageSigningChallenge), image hash and private key.
At first I tried to make a simple server returning image links using steemj-image-uploader but sadly, it didn't work. (The steemimage server just gave a message "invalid_signature" though I used right private key.) So, I digged steem-python codes.
The main problem was to sign a message because the steem image server always threw "invalid signature" when requesting a image link. I didn't know about signing process at all so decided to refer to "post" codes in steem-python/steem/commit and adjust them.
In the post codes, post data (json) is wrapped in "operations.Comment class" and put in the "transactionbuilder.TransactionBuilder" instance. The transaction builder is kinda helper for creating transaction and adding data(will be tranfered to the server) with sign in it.
class TransactionBuilder(dict):
""" This class simplifies the creation of transactions by adding
operations and signers.
"""
def __init__(self,
tx=None,
steemd_instance=None,
wallet_instance=None,
no_broadcast=False,
expiration=60):
self.steemd = steemd_instance or shared_steemd_instance()
self.no_broadcast = no_broadcast
self.expiration = expiration
self.wallet = wallet_instance or Wallet(self.steemd)
self.op = []
...
In the method "sign" in this class, we can find what actually have a roll for sign. (SignedTransaction class)
Link : steem-python/steem/transactionbuilder.py
class TransactionBuilder(dict):
...
def sign(self):
""" Sign a provided transaction witht he provided key(s)
:param dict tx: The transaction to be signed and returned
:param string wifs: One or many wif keys to use for signing
a transaction. If not present, the keys will be loaded
from the wallet as defined in "missing_signatures" key
of the transactions.
"""
# We need to set the default prefix, otherwise pubkeys are
# presented wrongly!
if self.steemd:
operations.default_prefix = self.steemd.chain_params["prefix"]
elif "blockchain" in self:
operations.default_prefix = self["blockchain"]["prefix"]
try:
signedtx = SignedTransaction(**self.json())
except Exception as e: # noqa FIXME(sneak)
raise e
if not any(self.wifs):
raise MissingKeyError
signedtx.sign(self.wifs, chain=self.steemd.chain_params) # <-- This one!!!
self["signatures"].extend(signedtx.json().get("signatures"))
The SignedTransaction instance is created with json data so when the sign method is called it just needs wifs(private keys) and chain data as parameters. In the sign method it tranforms the json data into hash data using the "DeriveDigest" method and makes a signature with private key. The "deriveDigest" method store hash data to the variable "digest" and byte message to the variable "message".
Link: steem-python/steembase/transactions.py
class SignedTransaction(GrapheneObject):
""" Create a signed transaction and offer method to create the
signature
:param num refNum: parameter ref_block_num (see ``getBlockParams``)
:param num refPrefix: parameter ref_block_prefix (see
``getBlockParams``)
:param str expiration: expiration date
:param Array operations: array of operations
"""
...
def sign(self, wifkeys, chain=None):
""" Sign the transaction with the provided private keys.
:param list wifkeys: Array of wif keys
:param str chain: identifier for the chain
"""
if not chain:
raise ValueError("Chain needs to be provided!")
self.deriveDigest(chain) # < --- This one !!
# Get Unique private keys
self.privkeys = []
[
self.privkeys.append(item) for item in wifkeys
if item not in self.privkeys
]
...
def deriveDigest(self, chain):
chain_params = self.getChainParams(chain)
# Chain ID
self.chainid = chain_params["chain_id"]
# Do not serialize signatures
sigs = self.data["signatures"]
self.data["signatures"] = []
# Get message to sign
# bytes(self) will give the wire formatted data according to
# GrapheneObject and the data given in __init__()
self.message = unhexlify(self.chainid) + compat_bytes(self)
self.digest = hashlib.sha256(self.message).digest()
# restore signatures
self.data["signatures"] = sigs
In summary,
What we need for getting a image link are
- image
- signature
To make a signature we need:
- image hash (digest) : to create this prefix and image are needed.
- private key
Functions we should need in SignedTransaction:
- sign() for making a signature
- deriveDigest() for creating data hash
- verify() for checking sig is valid. (optional)
However, as you see SignedTransaction class is fit to json data thus We should adjust some methods to make it appropriate for image data.
My plan was to make a new class derived from SignedTransaction class and adjust below methods(just little bit!):
- deriveDigest
- sign
- verify
Phew, writing is harder than coding..🤣 Plz give a comment if you have any questions.
Plus, let me know if some sentences are awkward. 🤣 Thank you!
I'll explain edited codes next articles! see ya!
2021.07.27 - [D.S/Project] - 210727tue - How to get image links from steemitimages server ? #2