I'm currently undertaking a course on Blockchain cryptography. And our first project is to transfer tokens from one wallet to another using client / server application. All mock of course.
This project is using the prescribed https://www.npmjs.com/package/ethereum-cryptography
The theory is this as I understand it:
Client side
- generate public and private key
- Create what I call an
action
object - {operation: "transfer", from (public key), to (public key), amount}
- Hash the action
- Sign the hash with private key to create a signature
- Post the
{action, signature}
to the server
Server side
- Verify the action using the signature and public key
- Perform the transfer between wallets (if valid)
I'm pretty sure that this is correct.
Now what I had to do required quite a bit of investigation and work arounds and it just feels like it should have been simpler. I'd like to know if there is a better way. Here is what I had to do:
Client side
- Client side - generate public and private key
- Create what I call an
action
- {operation: "transfer", from (public key), to (public key), amount}
- Hash the action with
keccak256(utf8ToBytes(JSON.stringify(action)))
- Sign the hash with private key to create a Signature using
secp256k1.sign(actionHash, privateKey)
- work around -> Because the returned Signature contains
bigint
s (which seemingly don't serialize), I had to use https://www.npmjs.com/package/json-bigint to stringify it
- Post the
{action, signature}
to the server
Server side
- work around -> Use
json-bigint
with { useNativeBigInt: true }
option to parse the signature (for BigInt(..)
in the next step)
- Whoops, I missed a detail previously. The (typed) signature of
verify
is:
verify: (signature: Hex | {
r: bigint;
s: bigint;
}, msgHash: Hex, publicKey: Hex, opts?: import("./abstract/weierstrass.js").VerOpts | undefined) => boolean;
and as I discuss below, I couldn't get the Hex version of signature
to work and despite it just seeming to take a {r: bigInt, s: bigInt}
, verify complained that I need to pass a Signature object.
class Signature {
constructor({r, s, recovery}) {
// const {r, s, recovery} = SigLikeObj;
this.r = BigInt(r);
this.s = BigInt(s);
this.recovery = recovery;
}
}
- Verify it with
secp256k1.verify(signatureFixed, actionHash, publicKey)
- Perform transfer
The json-bigint
requirement is a detail, but the need to create a class Signature
seems wrong.
I saw that the signature
in verify()
can be a Hex though I couldn't see a way to create this.
I tried this on the Server:
const signatureObj = JSONbig.parse(signatureJson);
const signatureHex = toHex(new Uint8Array(signatureObj))
const isValid = secp256k1.verify(signatureHex, actionHash, publicKey);
But verification fails this way.
Am I missing something - is there an easier way to handle the class Signature
or to make a Hex out of it?