Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App keys #6425

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions app/scripts/controllers/network/createMetamaskMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const createScaffoldMiddleware = require('json-rpc-engine/src/createScaffoldMidd
const createAsyncMiddleware = require('json-rpc-engine/src/createAsyncMiddleware')
const createWalletSubprovider = require('eth-json-rpc-middleware/wallet')

const namehash = require('eth-ens-namehash')
module.exports = createMetamaskMiddleware

function createMetamaskMiddleware ({
Expand All @@ -14,6 +15,12 @@ function createMetamaskMiddleware ({
processTypedMessageV3,
processPersonalMessage,
getPendingNonce,
appKey_eth_getPublicKey,
appKey_eth_getAddress,
appKey_eth_signMessage,
appKey_eth_signTransaction,
appKey_eth_signTypedMessage,
appKey_stark_signMessage
}) {
const metamaskMiddleware = mergeMiddleware([
createScaffoldMiddleware({
Expand All @@ -29,11 +36,21 @@ function createMetamaskMiddleware ({
processTypedMessageV3,
processPersonalMessage,
}),
createAppKeySubProvider(appKey_eth_getPublicKey,
appKey_eth_getAddress,
appKey_eth_signMessage,
appKey_eth_signTransaction,
appKey_eth_signTypedMessage,
appKey_stark_signMessage),
createPendingNonceMiddleware({ getPendingNonce }),
])
return metamaskMiddleware
}





function createPendingNonceMiddleware ({ getPendingNonce }) {
return createAsyncMiddleware(async (req, res, next) => {
if (req.method !== 'eth_getTransactionCount') return next()
Expand All @@ -43,3 +60,134 @@ function createPendingNonceMiddleware ({ getPendingNonce }) {
res.result = await getPendingNonce(address)
})
}



function createAppKeySubProvider (appKey_eth_getPublicKey,
appKey_eth_getAddress,
appKey_eth_signMessage,
appKey_eth_signTransaction,
appKey_eth_signTypedMessage,
appKey_stark_signMessage) {
return createScaffoldMiddleware({
'appKey_eth_getPublicKey': createAsyncMiddleware(appKeyEthGetPublicKey),
'appKey_eth_getAddress': createAsyncMiddleware(appKeyEthGetAddress),
'appKey_eth_signMessage': createAsyncMiddleware(appKeyEthSignMessage),
'appKey_eth_signTransaction': createAsyncMiddleware(appKeyEthSignTransaction),
'appKey_eth_signTypedMessage': createAsyncMiddleware(appKeyEthSignTypedMessage),
'appKey_stark_signMessage': createAsyncMiddleware(appKeyStarkSignMessage),
})

function prepareHdPath(origin, hdSubPath){
// beginning of Path using BIP 43 and arachnid eth subpurpose space
// Would prefer to use m/BIPNUMBER' once the app key eip is submitted as a bip
const beginningPath = "m/43'/60'/1775'"

// personaPath should be some option selected in Metamask itself
const personaPath = "0'"

// need to handle the origin for ENS access and for plugins (MetaMask)
// origin = "foo.bar.eth"

const uid = namehash.hash(origin)
const binUid = bits256HexToBin(uid)
const uidSubPath = splitBinUid(binUid)
const hdPath = beginningPath + "/" + personaPath + "/" + uidSubPath +"/" + hdSubPath
return hdPath
}

function bits256HexToBin(hex){

// strip and slice hex prefixed string to be under parseInt limit
// (under 53 bits, so we can slice 6 bytes ie 48 bits, so 12 chars)

// to slice 32 bytes (256 bits), we need:
// 5 slices * 48 bits = 240
// 1 slice * 16 bits

let bin = ""
for (let k = 0; k < 6; k++){
const subHex = hex.slice(2 + 12 * k, 12 * ( k + 1 ) + 2)
let subBits = parseInt(subHex, 16).toString(2)
if (k < 5){
subBits = subBits.padStart(48,"0")
}
else {
subBits = subBits.padStart(16,"0")
}
bin += subBits
}
return (bin)
}

function splitBinUid(binUid) {

// 256 bits binUid
// sliced as:
// 8 * 31 bits = 248 ==> 31 bits represented by 4 bytes, 32 bits, so 8 chars hex
// 1 * 8 bits ==> 1 byte, so 2 chars hex

console.log(binUid.length)
let numberOfSlices = 9
let subPath = ""
for (let k = 0; k < numberOfSlices; k++) {
if (k != numberOfSlices - 1) {
const binSlice = binUid.slice(31*k, 31*(k+1))
subPath += parseInt(binSlice, 2)
subPath += "'/"
}
if (k == numberOfSlices - 1) {
const binSlice = binUid.slice(31*k, 31 * k + 8)
subPath += parseInt(binSlice, 2)
subPath += "'"
}
}
return subPath
}




async function appKeyEthGetPublicKey(req, res) {
const hdSubPath = req.params
const hdPath = prepareHdPath(req.origin, hdSubPath)
res.result = await appKey_eth_getPublicKey(hdPath)
}

async function appKeyEthGetAddress(req, res) {
const hdSubPath = req.params
const hdPath = prepareHdPath(req.origin, hdSubPath)
res.result = await appKey_eth_getAddress(hdPath)
}

async function appKeyEthSignMessage(req, res) {
const hdSubPath = req.params[0]
const hdPath = prepareHdPath(req.origin, hdSubPath)
const message = req.params[1]
res.result = await appKey_eth_signMessage(hdPath, message)
}

async function appKeyEthSignTransaction(req, res) {
const hdSubPath = req.params[0]
const hdPath = prepareHdPath(req.origin, hdSubPath)
const txParams = req.params[1]
res.result = await appKey_eth_signTransaction(hdPath, txParams)
}

async function appKeyEthSignTypedMessage(req, res) {
const hdSubPath = req.params[0]
const hdPath = prepareHdPath(req.origin, hdSubPath)
const txParams = req.params[1]
res.result = await appKey_eth_signTypedMessage(hdPath, txParams)
}

async function appKeyStarkSignMessage(req, res) {
const hdSubPath = req.params[0]
const hdPath = prepareHdPath(req.origin, hdSubPath)
const message = req.params[1]
res.result = await appKey_stark_signMessage(hdPath, message)
}

}


55 changes: 55 additions & 0 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,15 @@ module.exports = class MetamaskController extends EventEmitter {
processTypedMessageV3: this.newUnsignedTypedMessage.bind(this),
processPersonalMessage: this.newUnsignedPersonalMessage.bind(this),
getPendingNonce: this.getPendingNonce.bind(this),

// app keys
appKey_eth_getPublicKey: this.appKey_eth_getPublicKey.bind(this),
appKey_eth_getAddress: this.appKey_eth_getAddress.bind(this),
appKey_eth_signTransaction: this.appKey_eth_signTransaction.bind(this),
appKey_eth_signMessage: this.appKey_eth_signMessage.bind(this),
appKey_eth_signTypedMessage: this.appKey_eth_signTypedMessage.bind(this),
appKey_stark_signMessage: this.appKey_stark_signMessage.bind(this),

}
const providerProxy = this.networkController.initializeProvider(providerOpts)
return providerProxy
Expand Down Expand Up @@ -1734,4 +1743,50 @@ module.exports = class MetamaskController extends EventEmitter {
this.providerApprovalController.setLocked()
return this.keyringController.setLocked()
}


/**
* App Keys
*/

// For now they only work with the HD keyring

async appKey_eth_getPublicKey (hdPath, next, end) {
const selectedKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
const pubKey = await this.keyringController.appKey_eth_getPublicKey(selectedKeyring, hdPath)
return pubKey
}

async appKey_eth_getAddress (hdPath, next, end) {
const selectedKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
const appKey = await this.keyringController.appKey_eth_getAddress(selectedKeyring, hdPath)
return appKey
}

async appKey_eth_signMessage (hdPath, message, next, end) {
const selectedKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
const sig = await this.keyringController.appKey_eth_signMessage(selectedKeyring, hdPath, message)
return sig
}

async appKey_eth_signTransaction (hdPath, txParams, next, end) {
const selectedKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
const sig = await this.keyringController.appKey_eth_signTransaction(selectedKeyring, hdPath, txParams)
return sig
}

async appKey_eth_signTypedMessage (hdPath, txParams, next, end) {
const selectedKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
const sig = await this.keyringController.appKey_eth_signTypedMessage(selectedKeyring, hdPath, txParams)
return sig
}

async appKey_stark_signMessage (hdPath, message, next, end) {
const selectedKeyring = this.keyringController.getKeyringsByType('HD Key Tree')[0]
const sig = await this.keyringController.appKey_stark_signMessage(selectedKeyring, hdPath, message)
return sig
}

}