Better transaction serializing API.
This commit is contained in:
parent
4514229f27
commit
e5d5871b95
@ -9,6 +9,8 @@ import { Signature } from './secp256k1';
|
||||
import errors = require('./errors');
|
||||
|
||||
|
||||
export const AddressZero = '0x0000000000000000000000000000000000000000';
|
||||
export const HashZero = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
export type Arrayish = string | ArrayLike<number>;
|
||||
|
||||
@ -240,29 +242,52 @@ export function hexZeroPad(value: string, length: number): string {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function splitSignature(signature: Arrayish): Signature {
|
||||
function isSignature(value: any): value is Signature {
|
||||
return (value && value.r != null && value.s != null);
|
||||
}
|
||||
|
||||
export function splitSignature(signature: Arrayish | Signature): Signature {
|
||||
let v = 0;
|
||||
let r = '0x', s = '0x';
|
||||
|
||||
if (isSignature(signature)) {
|
||||
r = hexZeroPad(signature.r, 32);
|
||||
s = hexZeroPad(signature.s, 32);
|
||||
|
||||
let recoveryParam = signature.recoveryParam;
|
||||
if (recoveryParam == null && signature.v != null) {
|
||||
recoveryParam = 1 - (signature.v % 2);
|
||||
}
|
||||
v = 27 + recoveryParam;
|
||||
|
||||
} else {
|
||||
let bytes: Uint8Array = arrayify(signature);
|
||||
if (bytes.length !== 65) {
|
||||
throw new Error('invalid signature');
|
||||
}
|
||||
r = hexlify(bytes.slice(0, 32));
|
||||
s = hexlify(bytes.slice(32, 64));
|
||||
|
||||
var v = bytes[64];
|
||||
v = bytes[64];
|
||||
if (v !== 27 && v !== 28) {
|
||||
v = 27 + (v % 2);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
r: hexlify(bytes.slice(0, 32)),
|
||||
s: hexlify(bytes.slice(32, 64)),
|
||||
r: r,
|
||||
s: s,
|
||||
recoveryParam: (v - 27),
|
||||
v: v
|
||||
}
|
||||
}
|
||||
|
||||
export function joinSignature(signature: Signature): string {
|
||||
signature = splitSignature(signature);
|
||||
|
||||
return hexlify(concat([
|
||||
hexZeroPad(signature.r, 32),
|
||||
hexZeroPad(signature.s, 32),
|
||||
signature.r,
|
||||
signature.s,
|
||||
(signature.recoveryParam ? '0x1c': '0x1b')
|
||||
]));
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
import { getAddress } from './address';
|
||||
import { BigNumber, bigNumberify, BigNumberish, ConstantZero } from './bignumber';
|
||||
import { arrayify, Arrayish, hexlify, hexZeroPad, stripZeros, } from './bytes';
|
||||
import { arrayify, Arrayish, hexlify, hexZeroPad, splitSignature, stripZeros, } from './bytes';
|
||||
import { keccak256 } from './keccak256';
|
||||
import { recoverAddress, Signature } from './secp256k1';
|
||||
import * as RLP from './rlp';
|
||||
@ -61,9 +61,7 @@ var transactionFields = [
|
||||
];
|
||||
|
||||
|
||||
export type SignDigestFunc = (digest: Uint8Array) => Signature;
|
||||
|
||||
export function serialize(transaction: UnsignedTransaction, signDigest?: SignDigestFunc): string {
|
||||
export function serialize(transaction: UnsignedTransaction, signature?: Arrayish | Signature): string {
|
||||
|
||||
var raw: Array<string | Uint8Array> = [];
|
||||
|
||||
@ -87,20 +85,22 @@ export function serialize(transaction: UnsignedTransaction, signDigest?: SignDig
|
||||
raw.push(hexlify(value));
|
||||
});
|
||||
|
||||
if (transaction.chainId && transaction.chainId !== 0) {
|
||||
if (transaction.chainId != null && transaction.chainId !== 0) {
|
||||
raw.push(hexlify(transaction.chainId));
|
||||
raw.push('0x');
|
||||
raw.push('0x');
|
||||
}
|
||||
|
||||
let unsignedTransaction = RLP.encode(raw);
|
||||
|
||||
// Requesting an unsigned transation
|
||||
if (!signDigest) {
|
||||
return RLP.encode(raw);
|
||||
if (!signature) {
|
||||
return unsignedTransaction;
|
||||
}
|
||||
|
||||
var digest = keccak256(RLP.encode(raw));
|
||||
|
||||
var signature = signDigest(arrayify(digest));
|
||||
// The splitSignature will ensure the transaction has a recoveryParam in the
|
||||
// case that the signTransaction function only adds a v.
|
||||
signature = splitSignature(signature);
|
||||
|
||||
// We pushed a chainId and null r, s on for hashing only; remove those
|
||||
var v = 27 + signature.recoveryParam
|
||||
|
@ -69,9 +69,10 @@ export class Wallet extends Signer {
|
||||
}
|
||||
|
||||
sign(transaction: TransactionRequest): Promise<string> {
|
||||
|
||||
return resolveProperties(transaction).then((tx) => {
|
||||
return serializeTransaction(tx, this.signingKey.signDigest.bind(this.signingKey));
|
||||
let rawTx = serializeTransaction(tx);
|
||||
let signature = this.signingKey.signDigest(keccak256(rawTx));
|
||||
return Promise.resolve(serializeTransaction(tx, signature));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -119,12 +119,16 @@ describe('Test Transaction Signing and Parsing', function() {
|
||||
assert.equal(parsedTransaction.chainId, 0, 'parses chainId (legacy)');
|
||||
|
||||
// Legacy serializes unsigned transaction
|
||||
assert.equal(ethers.utils.serializeTransaction(transaction), test.unsignedTransaction,
|
||||
{
|
||||
let unsignedTx = ethers.utils.serializeTransaction(transaction);
|
||||
assert.equal(unsignedTx, test.unsignedTransaction,
|
||||
'serializes undsigned transaction (legacy)');
|
||||
|
||||
// Legacy signed serialized transaction
|
||||
assert.equal(ethers.utils.serializeTransaction(transaction, signDigest), test.signedTransaction,
|
||||
let signature = signDigest(ethers.utils.keccak256(unsignedTx));
|
||||
assert.equal(ethers.utils.serializeTransaction(transaction, signature), test.signedTransaction,
|
||||
'signs transaction (legacy)');
|
||||
}
|
||||
|
||||
|
||||
// EIP155
|
||||
@ -152,13 +156,18 @@ describe('Test Transaction Signing and Parsing', function() {
|
||||
|
||||
transaction.chainId = 5;
|
||||
|
||||
// EIP-155 signed serialized transaction
|
||||
assert.equal(ethers.utils.serializeTransaction(transaction, signDigest), test.signedTransactionChainId5,
|
||||
'signs transaction (eip155)');
|
||||
|
||||
{
|
||||
// EIP-155 serialized unsigned transaction
|
||||
assert.equal(ethers.utils.serializeTransaction(transaction), test.unsignedTransactionChainId5,
|
||||
let unsignedTx = ethers.utils.serializeTransaction(transaction);
|
||||
assert.equal(unsignedTx, test.unsignedTransactionChainId5,
|
||||
'serializes unsigned transaction (eip155) ');
|
||||
|
||||
// EIP-155 signed serialized transaction
|
||||
let signature = signDigest(ethers.utils.keccak256(unsignedTx));
|
||||
assert.equal(ethers.utils.serializeTransaction(transaction, signature), test.signedTransactionChainId5,
|
||||
'signs transaction (eip155)');
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -5,6 +5,8 @@
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var errors = require("./errors");
|
||||
exports.AddressZero = '0x0000000000000000000000000000000000000000';
|
||||
exports.HashZero = '0x0000000000000000000000000000000000000000000000000000000000000000';
|
||||
function isBigNumber(value) {
|
||||
return !!value._bn;
|
||||
}
|
||||
@ -207,27 +209,46 @@ function hexZeroPad(value, length) {
|
||||
return value;
|
||||
}
|
||||
exports.hexZeroPad = hexZeroPad;
|
||||
function isSignature(value) {
|
||||
return (value && value.r != null && value.s != null);
|
||||
}
|
||||
function splitSignature(signature) {
|
||||
var v = 0;
|
||||
var r = '0x', s = '0x';
|
||||
if (isSignature(signature)) {
|
||||
r = hexZeroPad(signature.r, 32);
|
||||
s = hexZeroPad(signature.s, 32);
|
||||
var recoveryParam = signature.recoveryParam;
|
||||
if (recoveryParam == null && signature.v != null) {
|
||||
recoveryParam = 1 - (signature.v % 2);
|
||||
}
|
||||
v = 27 + recoveryParam;
|
||||
}
|
||||
else {
|
||||
var bytes = arrayify(signature);
|
||||
if (bytes.length !== 65) {
|
||||
throw new Error('invalid signature');
|
||||
}
|
||||
var v = bytes[64];
|
||||
r = hexlify(bytes.slice(0, 32));
|
||||
s = hexlify(bytes.slice(32, 64));
|
||||
v = bytes[64];
|
||||
if (v !== 27 && v !== 28) {
|
||||
v = 27 + (v % 2);
|
||||
}
|
||||
}
|
||||
return {
|
||||
r: hexlify(bytes.slice(0, 32)),
|
||||
s: hexlify(bytes.slice(32, 64)),
|
||||
r: r,
|
||||
s: s,
|
||||
recoveryParam: (v - 27),
|
||||
v: v
|
||||
};
|
||||
}
|
||||
exports.splitSignature = splitSignature;
|
||||
function joinSignature(signature) {
|
||||
signature = splitSignature(signature);
|
||||
return hexlify(concat([
|
||||
hexZeroPad(signature.r, 32),
|
||||
hexZeroPad(signature.s, 32),
|
||||
signature.r,
|
||||
signature.s,
|
||||
(signature.recoveryParam ? '0x1c' : '0x1b')
|
||||
]));
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ var transactionFields = [
|
||||
{ name: 'value', maxLength: 32 },
|
||||
{ name: 'data' },
|
||||
];
|
||||
function serialize(transaction, signDigest) {
|
||||
function serialize(transaction, signature) {
|
||||
var raw = [];
|
||||
transactionFields.forEach(function (fieldInfo) {
|
||||
var value = transaction[fieldInfo.name] || ([]);
|
||||
@ -52,17 +52,19 @@ function serialize(transaction, signDigest) {
|
||||
}
|
||||
raw.push(bytes_1.hexlify(value));
|
||||
});
|
||||
if (transaction.chainId && transaction.chainId !== 0) {
|
||||
if (transaction.chainId != null && transaction.chainId !== 0) {
|
||||
raw.push(bytes_1.hexlify(transaction.chainId));
|
||||
raw.push('0x');
|
||||
raw.push('0x');
|
||||
}
|
||||
var unsignedTransaction = RLP.encode(raw);
|
||||
// Requesting an unsigned transation
|
||||
if (!signDigest) {
|
||||
return RLP.encode(raw);
|
||||
if (!signature) {
|
||||
return unsignedTransaction;
|
||||
}
|
||||
var digest = keccak256_1.keccak256(RLP.encode(raw));
|
||||
var signature = signDigest(bytes_1.arrayify(digest));
|
||||
// The splitSignature will ensure the transaction has a recoveryParam in the
|
||||
// case that the signTransaction function only adds a v.
|
||||
signature = bytes_1.splitSignature(signature);
|
||||
// We pushed a chainId and null r, s on for hashing only; remove those
|
||||
var v = 27 + signature.recoveryParam;
|
||||
if (raw.length === 9) {
|
||||
|
@ -80,7 +80,9 @@ var Wallet = /** @class */ (function (_super) {
|
||||
Wallet.prototype.sign = function (transaction) {
|
||||
var _this = this;
|
||||
return properties_1.resolveProperties(transaction).then(function (tx) {
|
||||
return transaction_1.serialize(tx, _this.signingKey.signDigest.bind(_this.signingKey));
|
||||
var rawTx = transaction_1.serialize(tx);
|
||||
var signature = _this.signingKey.signDigest(keccak256_1.keccak256(rawTx));
|
||||
return Promise.resolve(transaction_1.serialize(tx, signature));
|
||||
});
|
||||
};
|
||||
Wallet.prototype.signMessage = function (message) {
|
||||
|
Loading…
Reference in New Issue
Block a user