I am trying to sign a raw transaction in javascript without relying on dependencies like web3, ethereumjs, etc.
I am following the python tutorial found here, and porting to JS as I go: https://lsongnotes.wordpress.com/2018/01/14/signing-an-ethereum-transaction-the-hard-way/
I start by building out the RLP-encoded transaction:
var FROM_PRIV = "00d862c318d05de0a1c25242c21989e15e35e70c55996fbc4238cd2f2f6a8f62";
//var FROM_ADDR = "8d900bfa2353548a4631be870f99939575551b60";
var NOUNCE = "80";
var GAS_PRICE = "85" + "0BA43B7400";
var GAS_LIMIT = "82" + "5208";
var TO = "94" + "7917bc33eea648809c285607579c9919fb864f8f";
var VALUE = "87" + "03BAF82D03A000";
var CODE = "80";
var EIP_155 = "018080";
//
var SIGN_DATA_NO_LEN = NOUNCE + GAS_PRICE + GAS_LIMIT + TO + VALUE + CODE + EIP_155;
var SIGN_DATA = "EB" + SIGN_DATA_NO_LEN;
console.log("SIGN_DATA " + SIGN_DATA);
Which correctly logs:
SIGN_DATA EB80850BA43B7400825208947917bc33eea648809c285607579c9919fb864f8f8703BAF82D03A00080018080
Next I convert this to a Message Hash for later signing:
var TXN = new Buffer.from(SIGN_DATA, 'hex');
var SIGNING_HASH = keccak256(TXN);//Edit: This is using pre-nist variation of keccack256 from sha3: https://github.com/emn178/js-sha3
console.log("SIGNING_HASH " + SIGNING_HASH);
This also correctly logs:
SIGNING_HASH a4060d01d4add248db470b4121616cbe5b2015daf328809000ec9a1d0954d649
So far everything seems to match the outputs from the python tutorial,
Next, I sign the message hash (aka SIGNING_HASH) with the private key (aka FROM_PRIV)
const SIGNATURE = secp256k1.ecdsaSign(Buffer.from(SIGNING_HASH, 'hex'), Buffer.from(FROM_PRIV, 'hex'));
console.log("SIGNATURE " + toHexString(SIGNATURE.signature));
var R_HEX = toHexString(SIGNATURE.signature).slice(0,64);
var S_HEX = toHexString(SIGNATURE.signature).slice(64,64*2);
console.log("RECIEVED R_HEX " + R_HEX);
console.log("RECIEVED S_HEX " + S_HEX);
console.log("EXPECTED R_HEX " + "067940651530790861714b2e8fd8b080361d1ada048189000c07a66848afde46");
console.log("EXPECTED S_HEX " + "69b041db7c29dbcc6becf42017ca7ac086b12bd53ec8ee494596f790fb6a0a69");
The output R and S values do not match what the tutorial generates, here is log:
RECIEVED R_HEX 4c1c84da0afb3b38984fcb56953481bc2c8262f3473e28591384f63baa5e979e
RECIEVED S_HEX 7aa01ea9cb0fc0af56102045c69b46d00c18e4f19fbb612c4ae43e13302c475d
EXPECTED R_HEX 067940651530790861714b2e8fd8b080361d1ada048189000c07a66848afde46
EXPECTED S_HEX 69b041db7c29dbcc6becf42017ca7ac086b12bd53ec8ee494596f790fb6a0a69
I have tried three different secp256k1
npm dependencies for ecdsa signing, they all produce this same incorrect output. (I am currently using const secp256k1 = require('secp256k1');
)
The expected signature from tutorial is 067940651530790861714b2e8fd8b080361d1ada048189000c07a66848afde4669b041db7c29dbcc6becf42017ca7ac086b12bd53ec8ee494596f790fb6a0a69
but I am getting signature 4c1c84da0afb3b38984fcb56953481bc2c8262f3473e28591384f63baa5e979e7aa01ea9cb0fc0af56102045c69b46d00c18e4f19fbb612c4ae43e13302c475d
I must be improperly formatting the SIGNING_HASH or Private key prior to signing, or is secp256k1-ecdsa not what I'm supposed to be using?
What's more interesting is, if I finish following the tutorial and build a signed TXN with these wrong R+S values, the txn works, I just broadcast one on main-net to confirm and the funds did move and received 12 confirmations already... does this imply that it doesn't necessarily matter that the R&S values from JS implementation of secp256k1 are different from what you get in python? The python tutorial also succeeded in broadcasting so it's unclear at this time... we both output different R,S values, but when push comes to shove we both succeeded in broadcasting? Very puzzling...
FROM_PRIV
start with0x
? What aboutSIGN_DATA
? In other words, make sure that whenever you doBuffer.from(str, 'hex')
, the value ofstr
consists of only hexadecimal digit characters, i.e., any of 0-9 and a-f and A-F (i.e., omit0x
if needed).Buffer.from(FROM_PRIV, 'hex')
so a0x
prefix would have to be striped anyways as far as I understand0x
prefix anywhere in the process at the moment.