From d3d24b609267086930a8c3fd46747f0e37edea46 Mon Sep 17 00:00:00 2001 From: JinGyeong Jeong Date: Wed, 13 Feb 2019 12:40:14 +0900 Subject: [PATCH] Change the EcdsaSignature type from the rsv interface to string --- integration_tests/Rpc.spec.ts | 9 +-- src/__test__/secp256k1.spec.ts | 39 ++++------- src/core/SignedTransaction.ts | 89 +++++-------------------- src/core/Transaction.ts | 8 +-- src/core/__test__/Transaction.spec.ts | 13 +--- src/core/transaction/Remove.ts | 7 +- src/core/transaction/Store.ts | 7 +- src/key/MemoryKeyStore.ts | 8 +-- src/key/__test__/LocalKeyStore.spec.ts | 8 +-- src/key/__test__/MemoryKeyStore.spec.ts | 14 ++-- src/utils.ts | 51 +++++++++----- 11 files changed, 81 insertions(+), 172 deletions(-) diff --git a/integration_tests/Rpc.spec.ts b/integration_tests/Rpc.spec.ts index 34d48a2f..e2d93b34 100644 --- a/integration_tests/Rpc.spec.ts +++ b/integration_tests/Rpc.spec.ts @@ -8,7 +8,6 @@ import { SignedTransaction, Transaction, TransferAsset, - U256, U64 } from "../lib/core/classes"; import { @@ -281,7 +280,8 @@ describe("rpc", () => { test("VerificationFailed", done => { const signed = tx.sign({ secret: signerSecret, fee: 10, seq }); - signed.r = new U256(0); + (signed as any)._signature = + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; sdk.rpc.chain .sendSignedTransaction(signed) .then(() => done.fail()) @@ -683,15 +683,12 @@ describe("rpc", () => { }); test("Ok", async () => { - const { r, s, v } = signEcdsa(message, secret); const signature = await sdk.rpc.account.sign( message, address, "my-password" ); - expect(signature).toContain(r); - expect(signature).toContain(s); - expect(signature).toContain(v); + expect(signature).toContain(signEcdsa(message, secret)); }); test("WrongPassword", async done => { diff --git a/src/__test__/secp256k1.spec.ts b/src/__test__/secp256k1.spec.ts index eb81acb2..0e1b2c37 100644 --- a/src/__test__/secp256k1.spec.ts +++ b/src/__test__/secp256k1.spec.ts @@ -1,8 +1,8 @@ import { - signEcdsa, - verifyEcdsa, + getPublicFromPrivate, recoverEcdsa, - getPublicFromPrivate + signEcdsa, + verifyEcdsa } from "../utils"; const priv = "99053a6568a93b9f194ef983c84ddfa9eb2b37888e47433558d40b2f4770b2d8"; @@ -15,23 +15,15 @@ test("public key", () => { test("sign", () => { const signature = signEcdsa(msg, priv); - expect(signature).toEqual({ - r: "d8706a863775325b1b8c3f16c19ff337c2699c4f857be903e08a5a9234c5a5c7", - s: "19d685ae28e52081480b08a3a1e5d8dd1f852b78f65a7e99af37ad42ebc5f375", - v: 0 - }); + expect(signature).toEqual( + "d8706a863775325b1b8c3f16c19ff337c2699c4f857be903e08a5a9234c5a5c719d685ae28e52081480b08a3a1e5d8dd1f852b78f65a7e99af37ad42ebc5f37500" + ); }); test("verify - success", () => { const result = verifyEcdsa( msg, - { - r: - "d8706a863775325b1b8c3f16c19ff337c2699c4f857be903e08a5a9234c5a5c7", - s: - "19d685ae28e52081480b08a3a1e5d8dd1f852b78f65a7e99af37ad42ebc5f375", - v: 0 - }, + "d8706a863775325b1b8c3f16c19ff337c2699c4f857be903e08a5a9234c5a5c719d685ae28e52081480b08a3a1e5d8dd1f852b78f65a7e99af37ad42ebc5f37500", getPublicFromPrivate(priv) ); expect(result).toBe(true); @@ -40,23 +32,16 @@ test("verify - success", () => { test("verify - fail", () => { const result = verifyEcdsa( "0000000000000000000000000000000000000000000000000000000000000000", - { - r: - "d8706a863775325b1b8c3f16c19ff337c2699c4f857be903e08a5a9234c5a5c7", - s: - "19d685ae28e52081480b08a3a1e5d8dd1f852b78f65a7e99af37ad42ebc5f375", - v: 0 - }, + "d8706a863775325b1b8c3f16c19ff337c2699c4f857be903e08a5a9234c5a5c719d685ae28e52081480b08a3a1e5d8dd1f852b78f65a7e99af37ad42ebc5f37500", getPublicFromPrivate(priv) ); expect(result).toBe(false); }); test("recover", () => { - const a = recoverEcdsa(msg, { - r: "d8706a863775325b1b8c3f16c19ff337c2699c4f857be903e08a5a9234c5a5c7", - s: "19d685ae28e52081480b08a3a1e5d8dd1f852b78f65a7e99af37ad42ebc5f375", - v: 0 - }); + const a = recoverEcdsa( + msg, + "d8706a863775325b1b8c3f16c19ff337c2699c4f857be903e08a5a9234c5a5c719d685ae28e52081480b08a3a1e5d8dd1f852b78f65a7e99af37ad42ebc5f37500" + ); expect(a).toBe(getPublicFromPrivate(priv)); }); diff --git a/src/core/SignedTransaction.ts b/src/core/SignedTransaction.ts index a686b562..1ca93458 100644 --- a/src/core/SignedTransaction.ts +++ b/src/core/SignedTransaction.ts @@ -1,4 +1,4 @@ -import { H160, H256, H512, PlatformAddress, U256 } from "codechain-primitives"; +import { H160, H256, H512, PlatformAddress } from "codechain-primitives"; import * as _ from "lodash"; import { blake160, blake256, recoverEcdsa } from "../utils"; @@ -24,64 +24,30 @@ const RLP = require("rlp"); * - A seq is not identical to the signer's seq. */ export class SignedTransaction { - /** - * Convert r, s, v values of an ECDSA signature to a string. - * @param params.r The r value of an ECDSA signature, which is up to 32 bytes of hexadecimal string. - * @param params.s The s value of an ECDSA signature, which is up to 32 bytes of hexadecimal string. - * @param params.v The recovery parameter of an ECDSA signature. - * @returns A 65 byte hexadecimal string. - */ - public static convertRsvToSignatureString(params: { - r: string; - s: string; - v: number; - }) { - const { r, s, v } = params; - return `0x${_.padStart(r, 64, "0")}${_.padStart( - s, - 64, - "0" - )}${_.padStart(v.toString(16), 2, "0")}`; - } - - private static convertSignatureStringToRsv( - signature: string - ): { r: string; s: string; v: number } { - if (signature.startsWith("0x")) { - signature = signature.substr(2); - } - const r = `0x${signature.substr(0, 64)}`; - const s = `0x${signature.substr(64, 64)}`; - const v = Number.parseInt(signature.substr(128, 2), 16); - return { r, s, v }; - } public unsigned: Transaction; - public v: number; - public r: U256; - public s: U256; public blockNumber: number | null; public blockHash: H256 | null; public transactionIndex: number | null; + private _signature: string; /** * @param unsigned A Transaction. - * @param sig An ECDSA signature which is a 65 byte hexadecimal string. + * @param signature An ECDSA signature which is a 65 byte hexadecimal string. * @param blockNumber The block number of the block that contains the tx. * @param blockHash The hash of the block that contains the tx. * @param transactionIndex The index(location) of the tx within the block. */ constructor( unsigned: Transaction, - sig: string, + signature: string, blockNumber?: number, blockHash?: H256, transactionIndex?: number ) { this.unsigned = unsigned; - const { r, s, v } = SignedTransaction.convertSignatureStringToRsv(sig); - this.v = v; - this.r = new U256(r); - this.s = new U256(s); + this._signature = signature.startsWith("0x") + ? signature.substr(2) + : signature; this.blockNumber = blockNumber === undefined ? null : blockNumber; this.blockHash = blockHash || null; this.transactionIndex = @@ -92,22 +58,16 @@ export class SignedTransaction { * Get the signature of a tx. */ public signature() { - const { v, r, s } = this; - return { v, r, s }; + return this._signature; } /** * Convert to an object for RLP encoding. */ public toEncodeObject(): any[] { - const { unsigned, v, r, s } = this; - const sig = `0x${_.padStart(r.value.toString(16), 64, "0")}${_.padStart( - s.value.toString(16), - 64, - "0" - )}${_.padStart(v.toString(16), 2, "0")}`; + const { unsigned, _signature } = this; const result = unsigned.toEncodeObject(); - result.push(sig); + result.push(`0x${_signature}`); return result; } @@ -137,12 +97,8 @@ export class SignedTransaction { * @deprecated */ public getSignerAccountId(): H160 { - const { r, s, v, unsigned } = this; - const publicKey = recoverEcdsa(unsigned.hash().value, { - r: r.value.toString(16), - s: s.value.toString(16), - v - }); + const { _signature, unsigned } = this; + const publicKey = recoverEcdsa(unsigned.hash().value, _signature); return new H160(blake160(publicKey)); } @@ -160,14 +116,8 @@ export class SignedTransaction { * @returns A public key. */ public getSignerPublic(): H512 { - const { r, s, v, unsigned } = this; - return new H512( - recoverEcdsa(unsigned.hash().value, { - r: r.value.toString(16), - s: s.value.toString(16), - v - }) - ); + const { _signature, unsigned } = this; + return new H512(recoverEcdsa(unsigned.hash().value, _signature)); } /** @@ -180,20 +130,13 @@ export class SignedTransaction { blockHash, transactionIndex, unsigned, - v, - r, - s + _signature } = this; - const sig = SignedTransaction.convertRsvToSignatureString({ - r: r.value.toString(16), - s: s.value.toString(16), - v - }); const result = unsigned.toJSON(); result.blockNumber = blockNumber; result.blockHash = blockHash === null ? null : blockHash.toJSON(); result.transactionIndex = transactionIndex; - result.sig = sig; + result.sig = `0x${_signature}`; result.hash = this.hash().toJSON(); return result; } diff --git a/src/core/Transaction.ts b/src/core/Transaction.ts index 14a5d5cf..151066c4 100644 --- a/src/core/Transaction.ts +++ b/src/core/Transaction.ts @@ -85,12 +85,10 @@ export abstract class Transaction { throw Error("The tx fee is already set"); } this._fee = U64.ensure(fee); - const { r, s, v } = signEcdsa( - this.hash().value, - H256.ensure(secret).value + return new SignedTransaction( + this, + signEcdsa(this.hash().value, H256.ensure(secret).value) ); - const sig = SignedTransaction.convertRsvToSignatureString({ r, s, v }); - return new SignedTransaction(this, sig); } public toJSON() { diff --git a/src/core/__test__/Transaction.spec.ts b/src/core/__test__/Transaction.spec.ts index 6e371baf..9337b050 100644 --- a/src/core/__test__/Transaction.spec.ts +++ b/src/core/__test__/Transaction.spec.ts @@ -83,17 +83,8 @@ test("sign", () => { seq: 0, fee: 0 }); - const { v, r, s } = signed.signature(); - expect(v).toBe(1); - expect(r.toEncodeObject()).toEqual( - new U256( - "0x3f9bcff484bd5f1d5549f912f9eeaf8c2fe349b257bde2b61fb1036013d4e44c" - ).toEncodeObject() - ); - expect(s.toEncodeObject()).toEqual( - new U256( - "0x204a4215d26cb879eaad2028fe1a7898e4cf9a5d979eb383e0a384140d6e04c1" - ).toEncodeObject() + expect(signed.signature()).toBe( + "3f9bcff484bd5f1d5549f912f9eeaf8c2fe349b257bde2b61fb1036013d4e44c204a4215d26cb879eaad2028fe1a7898e4cf9a5d979eb383e0a384140d6e04c101" ); }); diff --git a/src/core/transaction/Remove.ts b/src/core/transaction/Remove.ts index f0668df4..d25dbca3 100644 --- a/src/core/transaction/Remove.ts +++ b/src/core/transaction/Remove.ts @@ -26,12 +26,7 @@ export class Remove extends Transaction { if ("secret" in params) { const { hash, secret } = params; this._hash = hash; - const { r, s, v } = signEcdsa(hash.value, secret.value); - this.signature = `${_.padStart(r, 64, "0")}${_.padStart( - s, - 64, - "0" - )}${_.padStart(v.toString(16), 2, "0")}`; + this.signature = signEcdsa(hash.value, secret.value); } else { let signature = params.signature; if (signature.startsWith("0x")) { diff --git a/src/core/transaction/Store.ts b/src/core/transaction/Store.ts index b11a2b4a..e97c683c 100644 --- a/src/core/transaction/Store.ts +++ b/src/core/transaction/Store.ts @@ -34,15 +34,10 @@ export class Store extends Transaction { getPublicFromPrivate(secret.value), { networkId } ); - const { r, s, v } = signEcdsa( + this.signature = signEcdsa( blake256(RLP.encode(content)), secret.value ); - this.signature = `${_.padStart(r, 64, "0")}${_.padStart( - s, - 64, - "0" - )}${_.padStart(v.toString(16), 2, "0")}`; } else { const { content, certifier } = params; let signature = params.signature; diff --git a/src/key/MemoryKeyStore.ts b/src/key/MemoryKeyStore.ts index c88d7b82..3f88bee7 100644 --- a/src/key/MemoryKeyStore.ts +++ b/src/key/MemoryKeyStore.ts @@ -80,13 +80,7 @@ class KeyManager implements KeyManagementAPI { if (passphrase !== this.passphraseMap[key]) { return Promise.reject("The passphrase does not match"); } - const { r, s, v } = signEcdsa(message, this.privateKeyMap[key]); - const sig = `${_.padStart(r, 64, "0")}${_.padStart( - s, - 64, - "0" - )}${_.padStart(v.toString(16), 2, "0")}`; - return Promise.resolve(sig); + return Promise.resolve(signEcdsa(message, this.privateKeyMap[key])); } } diff --git a/src/key/__test__/LocalKeyStore.spec.ts b/src/key/__test__/LocalKeyStore.spec.ts index d7d5eb2c..85089584 100644 --- a/src/key/__test__/LocalKeyStore.spec.ts +++ b/src/key/__test__/LocalKeyStore.spec.ts @@ -56,10 +56,6 @@ test("sign", async () => { key, message }); - const r = `${signature.substr(0, 64)}`; - const s = `${signature.substr(64, 64)}`; - const v = Number.parseInt(signature.substr(128, 2), 16); - - expect(verifyEcdsa(message, { r, s, v }, publicKey)).toBe(true); - expect(recoverEcdsa(message, { r, s, v })).toEqual(publicKey); + expect(verifyEcdsa(message, signature, publicKey)).toBe(true); + expect(recoverEcdsa(message, signature)).toEqual(publicKey); }); diff --git a/src/key/__test__/MemoryKeyStore.spec.ts b/src/key/__test__/MemoryKeyStore.spec.ts index b281e52f..902975af 100644 --- a/src/key/__test__/MemoryKeyStore.spec.ts +++ b/src/key/__test__/MemoryKeyStore.spec.ts @@ -1,8 +1,8 @@ import { - recoverEcdsa, - verifyEcdsa, + getAccountIdFromPublic, getPublicFromPrivate, - getAccountIdFromPublic + recoverEcdsa, + verifyEcdsa } from "../../utils"; import { MemoryKeyStore } from "../MemoryKeyStore"; @@ -53,10 +53,6 @@ test("sign", async () => { key, message }); - const r = `${signature.substr(0, 64)}`; - const s = `${signature.substr(64, 64)}`; - const v = Number.parseInt(signature.substr(128, 2), 16); - - expect(verifyEcdsa(message, { r, s, v }, publicKey)).toBe(true); - expect(recoverEcdsa(message, { r, s, v })).toEqual(publicKey); + expect(verifyEcdsa(message, signature, publicKey)).toBe(true); + expect(recoverEcdsa(message, signature)).toEqual(publicKey); }); diff --git a/src/utils.ts b/src/utils.ts index 2c55eb1d..30861ca7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -15,6 +15,7 @@ import { toHex as _toHex, verifyEcdsa as _verifyEcdsa } from "codechain-primitives"; +import * as _ from "lodash"; /** * Converts buffer to hexadecimal string. @@ -164,44 +165,62 @@ const encodeSignatureTagOutput = (output: number[]) => { return bytes.reverse(); }; -export interface EcdsaSignature { - r: string; - s: string; - v: number; -} +export type EcdsaSignature = string; /** * Gets signature for message from private key. * @param message arbitrary length string - * @param priv 32 byte hexadecimal string of private key - * @returns r, s, v of ECDSA signature - */ -export const signEcdsa = (message: string, priv: string): EcdsaSignature => - _signEcdsa(message, priv); + * @param priv 32 byte hexstring of private key + * @returns 65 byte hexstring of ECDSA signature + */ +export const signEcdsa = (message: string, priv: string): EcdsaSignature => { + const { r, s, v } = _signEcdsa(message, priv); + return `${_.padStart(r, 64, "0")}${_.padStart(s, 64, "0")}${_.padStart( + v.toString(16), + 2, + "0" + )}`; +}; /** * Checks if the signature from signEcdsa is correct. * @param message arbitrary length string - * @param signature r, s, v of ECDSA signature - * @param pub 64 byte hexadecimal string of public key + * @param signature 65 byte hexstring of ECDSA signature + * @param pub 64 byte hexstring of public key * @returns if signature is valid, true. Else false. */ export const verifyEcdsa = ( message: string, signature: EcdsaSignature, pub: string -): boolean => _verifyEcdsa(message, signature, pub); +): boolean => { + if (signature.startsWith("0x")) { + signature = signature.substr(2); + } + const r = signature.substr(0, 64); + const s = signature.substr(64, 64); + const v = Number.parseInt(signature.substr(128, 2), 16); + return _verifyEcdsa(message, { r, s, v }, pub); +}; /** * Gets public key from the message and signature. * @param message arbitrary length string - * @param signature r, s, v of ECDSA signature - * @returns 64 byte hexadecimal string public key + * @param signature 65 byte hexstring of ECDSA signature + * @returns 64 byte hexstring public key */ export const recoverEcdsa = ( message: string, signature: EcdsaSignature -): string => _recoverEcdsa(message, signature); +): string => { + if (signature.startsWith("0x")) { + signature = signature.substr(2); + } + const r = signature.substr(0, 64); + const s = signature.substr(64, 64); + const v = Number.parseInt(signature.substr(128, 2), 16); + return _recoverEcdsa(message, { r, s, v }); +}; /** * Generates a private key.