Skip to content
This repository has been archived by the owner on Jul 23, 2024. It is now read-only.

Commit

Permalink
Merge pull request #107 from jimni1222/optional
Browse files Browse the repository at this point in the history
Make privateKey optional in signTransaction
  • Loading branch information
jimni1222 authored Sep 23, 2019
2 parents 07ce935 + 0789b13 commit bf0f04e
Show file tree
Hide file tree
Showing 6 changed files with 409 additions and 72 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"build": "gulp default",
"serTest": "mocha test/transactionType/serializationTest.js && mocha test/compressPublicKey.js && mocha test/encodeContractDeploy.js && mocha test/isCompressedPublicKey.js && mocha test/parseAccountKey.js && mocha test/decodeTransaction.js",
"walletTest": "mocha test/accountLib.js && mocha test/accounts.privateKeyToPublicKey.js && mocha test/accounts.recover.js && mocha test/packages/caver.klay.accounts.js && mocha test/getKlaytnWalletKey.js && mocha test/isValidPrivateKey.js && mocha test/privateKeyToAccount.js",
"txTest": "mocha test/estimateComputationCost.js && mocha test/getTransactionReceipt.js && mocha test/getTransaction.js && mocha test/setContractOptions.js && mocha test/encodeContractDeploy.js && mocha test/accounts.signTransaction.js && mocha test/sendTransactionCallback.js && mocha test/transactionType/legacyTransaction.js && mocha test/transactionType/valueTransfer* && mocha test/transactionType/accountUpdate.js && mocha test/transactionType/contract* && mocha test/transactionType/cancelTransaction.js && mocha test/transactionType/feeDelegated*",
"txTest": "mocha test/estimateComputationCost.js && mocha test/getTransactionReceipt.js && mocha test/getTransaction.js && mocha test/setContractOptions.js && mocha test/encodeContractDeploy.js && mocha test/accounts.signTransaction.js && mocha test/sendTransactionCallback.js && mocha test/signWithMultiSig.js && mocha test/transactionType/legacyTransaction.js && mocha test/transactionType/valueTransfer* && mocha test/transactionType/accountUpdate.js && mocha test/transactionType/contract* && mocha test/transactionType/cancelTransaction.js && mocha test/transactionType/feeDelegated*",
"etcTest": "mocha test/packages/caver.utils.js && mocha test/confirmationListener.js && mocha test/hashMessage.js && mocha test/iban.* && mocha test/randomHex.js && mocha test/sha3.js && mocha test/toChecksumAddress.js && mocha test/unitMap.js && mocha test/default* && mocha test/getNodeInfo.js && mocha test/eventEmitter.js && mocha test/packages/caver.klay.net.js && mocha test/getNetworkType.js && mocha test/invalidResponse.js && mocha test/isContractDeployment.js && mocha test/personal.js && mocha test/multiProviderTest.js && mocha test/subscription.js && mocha test/supportsSubscriptions.js && mocha test/contract.once.js && mocha test/setProvider.js",
"intTxTest": "npm run intLEGACYTest && npm run intVTTest && npm run intVTMTest && npm run intACCUPTest && npm run intDEPLTest && npm run intEXETest && npm run intCANCELTest && npm run intFDTest && npm run intFDRTest",
"intLEGACYTest": "mocha --grep INT-LEGACY/ test/intTest.js",
Expand Down
29 changes: 8 additions & 21 deletions packages/caver-core-method/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,25 +246,6 @@ function toPayload (args) {
return (this.transformPayload && this.transformPayload(payload)) || payload
}

var getWallet = function(from, accounts) {
let wallet = null

// is index given
if (_.isNumber(from)) {
wallet = accounts.wallet[from]

// is account given
} else if (_.isObject(from) && from.address && from.privateKey) {
wallet = from

// search in wallet for address
} else {
wallet = accounts.wallet[from.toLowerCase()]
}

return wallet
}

const buildSendTxCallbackFunc = (defer, method, payload, isSendTx) => (err, result) => {
try { result = method.formatOutput(result) }
catch (e) {
Expand Down Expand Up @@ -316,12 +297,18 @@ const buildSendRequestFunc = (defer, sendSignedTx, sendTxCallback) => (payload,
// return method.accounts.sendTransactionWithSignature(tx).then(sendSignedTx)
// }

if (!_.isObject(tx)) {
let error = new Error('The transaction must be defined as an object.')
sendTxCallback(error)
return Promise.reject(error)
}

if (tx.senderRawTransaction && tx.from && tx.feePayer){
console.log('"from" is ignored for a fee-delegated transaction.')
delete tx.from
}

const wallet = getWallet(_.isObject(tx) && tx.from || tx.feePayer || null, method.accounts)
const wallet = method.accounts.wallet.getAccount(tx.from || tx.feePayer)

if (wallet && wallet.privateKey) {
// If wallet was found, sign tx, and send using sendRawTransaction
Expand Down Expand Up @@ -351,7 +338,7 @@ const buildSendRequestFunc = (defer, sendSignedTx, sendTxCallback) => (payload,
}
case 'klay_sign': {
const data = payload.params[1]
const wallet = getWallet(payload.params[0], method.accounts)
const wallet = method.accounts.wallet.getAccount(payload.params[0])

if (wallet && wallet.privateKey) {
// If wallet was found, sign tx, and send using sendRawTransaction
Expand Down
133 changes: 96 additions & 37 deletions packages/caver-klay/caver-klay-accounts/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,50 +191,85 @@ Accounts.prototype.getLegacyAccount = function getLegacyAccount(key) {
return { legacyAccount: Account.fromPrivate(privateKey), klaytnWalletKeyAddress }
}

Accounts.prototype.signTransaction = function signTransaction(tx, privateKey, callback) {
Accounts.prototype.signTransaction = function signTransaction() {
var _this = this,
error = false,
isLegacy = false,
parsed,
result
result,
callback

callback = callback || function () {}
let handleError = (e) => {
e = e instanceof Error? e : new Error(e)
if (callback) callback(e)
return Promise.reject(e)
}

if (!tx) {
error = new Error('No transaction object given!')
callback(error)
return Promise.reject(error)
if (arguments.length === 0 || arguments.length > 3) {
return handleError('Invalid parameter: The number of parameters is invalid.')
}

// privateKey and callback are optional parameter
// "arguments.length === 2" means that user sent parameter privateKey or callback
let tx = arguments[0], privateKey

try {
parsed = utils.parsePrivateKey(privateKey)
privateKey = parsed.privateKey
} catch(e) {
callback(e)
return Promise.reject(e)
if (!tx || !_.isObject(tx)) {
return handleError('Invalid parameter: The transaction must be defined as an object')
}
if (!utils.isValidPrivateKey(privateKey)) {
error = new Error('Invalid private key')
callback(error)
return Promise.reject(error)

if (arguments.length === 2) {
if (_.isFunction(arguments[1])) {
callback = arguments[1]
} else {
privateKey = arguments[1]
}
} else if (arguments.length === 3) {
if (typeof arguments[1] !== 'string' && !_.isArray(arguments[1])){
return handleError('Invalid parameter: The parameter for the private key is invalid')
}
privateKey = arguments[1]
callback = arguments[2]
}

privateKey = utils.addHexPrefix(privateKey)
// For handling when callback is undefined.
callback = callback || function () {}

function signed(tx) {
// Validate tx object
if (!tx.senderRawTransaction) {
const error = helpers.validateFunction.validateParams(tx)
if (error) return handleError(error)
} else if (!tx.feePayer) { return handleError('To sign with fee payer, senderRawTransaction and feePayer must be defined in the transaction object.') }

if (!tx.senderRawTransaction) {
error = helpers.validateFunction.validateParams(tx)

// Attempting to sign with decoupled account into a legacy type transaction throw an error.
isLegacy = tx.type === undefined || tx.type === 'LEGACY' ? true : false
if (!error && isLegacy && _this.isDecoupled(privateKey, tx.from)) error = new Error('A legacy transaction must be with a legacy account key')
// When privateKey is undefined, find Account from Wallet.
if (privateKey === undefined) {
try {
const account = this.wallet.getAccount(tx.from || tx.feePayer)
privateKey = account.privateKey
} catch(e) {
return handleError(e)
}
if (error) {
callback(error);
return Promise.reject(error);
}

let privateKeys = _.isArray(privateKey) ? privateKey : [privateKey]

try {
for (let i = 0; i < privateKeys.length; i ++) {
const parsed = utils.parsePrivateKey(privateKeys[i])
privateKeys[i] = parsed.privateKey
privateKeys[i] = utils.addHexPrefix(privateKeys[i])

if (!utils.isValidPrivateKey(privateKeys[i])) return handleError('Invalid private key')
}
} catch(e) {
return handleError(e)
}

// Attempting to sign with a decoupled account into a legacy type transaction should be rejected.
if (!tx.senderRawTransaction) {
isLegacy = tx.type === undefined || tx.type === 'LEGACY' ? true : false
if (isLegacy && privateKeys.length > 1) return handleError('Legacy transaction cannot signed with multiple keys')
if (isLegacy && _this.isDecoupled(privateKeys[0], tx.from)) return handleError('A legacy transaction must be with a legacy account key')
}

function signed(tx) {
try {
// Guarantee all property in transaction is hex.
tx = helpers.formatters.inputCallFormatter(tx)
Expand All @@ -245,21 +280,32 @@ Accounts.prototype.signTransaction = function signTransaction(tx, privateKey, ca

const messageHash = Hash.keccak256(rlpEncoded)

const signature = Account.makeSigner(Nat.toNumber(transaction.chainId || "0x1") * 2 + 35)(messageHash, privateKey)
const [v, r, s] = Account.decodeSignature(signature).map(sig => utils.makeEven(utils.trimLeadingZero(sig)))
let signatures = []

const rawTransaction = makeRawTransaction(rlpEncoded, [v, r, s], transaction)
for(const privateKey of privateKeys) {
const signature = Account.makeSigner(Nat.toNumber(transaction.chainId || "0x1") * 2 + 35)(messageHash, privateKey)
const [v, r, s] = Account.decodeSignature(signature).map(sig => utils.makeEven(utils.trimLeadingZero(sig)))
signatures.push([v, r, s])
}

const rawTransaction = makeRawTransaction(rlpEncoded, signatures, transaction)

result = {
messageHash: messageHash,
v: v,
r: r,
s: s,
v: signatures[0][0],
r: signatures[0][1],
s: signatures[0][2],
rawTransaction: rawTransaction,
txHash: Hash.keccak256(rawTransaction),
senderTxHash: getSenderTxHash(rawTransaction),
}

if (tx.senderRawTransaction && tx.feePayer) {
result.feePayerSignatures = signatures
} else {
result.signatures = isLegacy? signatures[0] : signatures
}

} catch(e) {
callback(e)
return Promise.reject(e)
Expand Down Expand Up @@ -289,7 +335,7 @@ Accounts.prototype.signTransaction = function signTransaction(tx, privateKey, ca
return Promise.all([
isNot(tx.chainId) ? _this._klaytnCall.getChainId() : tx.chainId,
isNot(tx.gasPrice) ? _this._klaytnCall.getGasPrice() : tx.gasPrice,
isNot(tx.nonce) ? _this._klaytnCall.getTransactionCount(tx.from || _this.privateKeyToAccount(privateKey).address) : tx.nonce
isNot(tx.nonce) ? _this._klaytnCall.getTransactionCount(tx.from) : tx.nonce
]).then(function (args) {
if (isNot(args[0]) || isNot(args[1]) || isNot(args[2])) {
throw new Error('One of the values "chainId", "gasPrice", or "nonce" couldn\'t be fetched: '+ JSON.stringify(args));
Expand Down Expand Up @@ -1053,6 +1099,19 @@ Wallet.prototype.getKlaytnWalletKey = function (addressOrIndex) {
return genKlaytnWalletKeyStringFromAccount(account)
}

Wallet.prototype.getAccount = function (input) {
if (_.isNumber(input)) {
if (this.length <= input) throw new Error(`The index(${input}) is out of range(Wallet length : ${this.length}).`)
return this[input]
}

if (!_.isString(input)) throw new Error(`Accounts in the Wallet can be searched by only index or address.`)

if (!utils.isAddress(input)) throw new Error(`Failed to getAccount from Wallet: invalid address(${input})`)

return this[input.toLowerCase()]
}

function genKlaytnWalletKeyStringFromAccount(account) {
var addressString = account.address
var privateKey = account.privateKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ function makeRawTransaction(rlpEncoded, sig, transaction) {
}
case 'LEGACY':
default:
rawTx = decodedValues.slice(0, 6).concat(sig)
rawTx = decodedValues.slice(0, 6).concat(sig[0])
return RLP.encode(rawTx)
}
}
Expand Down
Loading

0 comments on commit bf0f04e

Please sign in to comment.