palladians / pallad

Progressive Mina Protocol Wallet
https://pallad.co/
Apache License 2.0
25 stars 8 forks source link

Experimental: Session key agent & provider method #176

Closed mich3lang3lo closed 4 months ago

mich3lang3lo commented 4 months ago

Describe changes

Session keys are a big feature that are still experimental. In this PR I have a new SessionKeyAgentBase which can be used by an application like a zkApp to derive a new random private key credential for a session and also an experimental web-provider listener that can receive a experimental_requestSession request with data that includes the request of signing a merkle root of the session's parameters:

export interface offchainSessionAllowedMethods {
  contractAddress: string
  method: string
}

export type requestOffchainSession = {
  data: {
    sessionKey: string
    expirationTime: string
    allowedMethods: offchainSessionAllowedMethods[]
    sessionMerkleRoot: MinaSignablePayload
  }
}

Because the user cannot sign the object itself the application can ask the wallet to sign the merkle root of the session data tree one time, enabling the the application to use an instance of SessionKeyAgentBase to sign operations like this but only pseudocode:


class SessionKeyAgentBaseInstance extends SessionKeyAgentBase {}
const networkType = "testnet"
const instance = new SessionKeyAgentBaseInstance()

const args: MinaDerivationArgs = {
  network: Network.Mina,
  accountIndex: Math.floor(Math.random() * 10),
  addressIndex: Math.floor(Math.random() * 10),
}

const sessionCredential = await instance.deriveCredentials(args)
const sessionParams = {
  sessionKey: "B62..flsw",
  expirationTime: "14950204",
  allowedMethods: [
            {
              contractAddress: "B62..dow"
              method: "MoveChessPieces"
            }
         ]
}
const requestedSessionParams =  {
  data: sessionParams
    sessionMerkleRoot: toMerkleTree(sessionParams).getRoot()
  }
}

// request wallet to sign the root
const accounts = await window.mina.request({method: 'mina_enable'})
const signedRoot = await window.mina.request({method: 'experimental_requestSession', params: requestedSessionParams})

// commit signedRoot to contract
const tx = await Mina.transaction(accounts[0], async () => {
  await chessZkApp.commitSession(signedRoot, accounts[0]);
});

// request last signature form wallet
const signedRoot = await window.mina.request({method: 'mina_signTransaction', params: tx})

// sign many transactions:
for (move in chessGameMoves) {
  const chessMovetx = await Mina.transaction(accounts[0], async () => {
    await chessZkApp.chessMove(move);
  });
  const signedTx = await instance.sign(groupedCredential, chessMovetx, {
          network: Network.Mina,
          networkType: "testnet",
          operation: "mina_signTransaction",
        })
  await submitTxProvider(signedTx)
}

I think it would be best for smart contract engineers to include the complexity of constraining the session key's spending in their methods.