Skip to content

Commit

Permalink
Transfer endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
serefyarar committed May 1, 2024
1 parent 3b5ac82 commit 7dd95d2
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 32 deletions.
60 changes: 47 additions & 13 deletions api/src/controllers/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getAddress } from "@ethersproject/address";
import axios from "axios";
import RedisClient from '../clients/redis.js';
import { getPKPSession, getRolesFromSession, mintPKP, writeAuthMethods } from "../libs/lit/index.js";
import { getOwner, getPKPSession, getRolesFromSession, mintPKP, transferOwnership, writeAuthMethods } from "../libs/lit/index.js";
import { getAuthSigFromDIDSession } from "../utils/helpers.js";
import { DIDService } from "../services/did.js";
import { IndexService } from "../services/index.js";
Expand All @@ -10,6 +10,7 @@ const redis = RedisClient.getInstance();

export const getIndexById = async (req, res, next) => {
try {

const indexService = new IndexService().setSession(req.session);
const index = await indexService.getIndexById(req.params.id);

Expand Down Expand Up @@ -39,33 +40,32 @@ export const getIndexById = async (req, res, next) => {
}
export const createIndex = async (req, res, next) => {
try {
let timers = {};
console.time("createIndex")
const indexParams = req.body;

const ownerWallet = req.session.did.parent.split(":").pop();

console.time("mintPKP")
const newPKP = await mintPKP(ownerWallet, req.body.signerFunction);
timers.mintPKP = console.timeEnd("mintPKP")
indexParams.signerPublicKey = newPKP.pkpPublicKey;

console.time("getPKPSession")
const pkpSession = await getPKPSession(req.session, indexParams);
timers.getPKPSession = console.timeEnd("getPKPSession")
const indexService = new IndexService().setSession(pkpSession); //PKP

console.log(pkpSession.serialize())
const indexService = new IndexService().setSession(pkpSession); //PKP
let newIndex = await indexService.createIndex(indexParams);

console.log(newIndex);
if(!newIndex){
return res.status(500).json({ error: "Create index error" });
}

//Cache pkp session after index creation.
const sessionCacheKey = `${req.session.did.parent}:${newIndex.id}:${newIndex.signerFunction}`
const sessionCacheKey = `${req.session.did.parent}:${ownerWallet}:${newIndex.id}:${newIndex.signerFunction}`
console.log("hellodear", sessionCacheKey)
await redis.hSet("sessions", sessionCacheKey, pkpSession.serialize());

const didService = new DIDService().setSession(req.session); //Personal
const newIndexDID = await didService.setDIDIndex(newIndex.id, "owned");
await didService.setDIDIndex(newIndex.id, "owned");

newIndex = await indexService.getIndexById(newIndex.id);

newIndex.did = {
Expand All @@ -77,8 +77,6 @@ export const createIndex = async (req, res, next) => {
creator: true
}

timers.createIndex = console.timeEnd("createIndex")
console.log(timers)
res.status(201).json(newIndex);
} catch (error) {
res.status(500).json({ error: error.message });
Expand All @@ -101,7 +99,7 @@ export const updateIndex = async (req, res, next) => {
if(vals){
index.signerFunction = req.body.signerFunction;
}else{
res.status(500).json({ error: "Unauthorized" });
return res.status(500).json({ error: "Unauthorized" });
}
}

Expand All @@ -127,6 +125,42 @@ export const updateIndex = async (req, res, next) => {
res.status(500).json({ error: error.message });
}
};

export const transferIndex = async (req, res, next) => {
try {



const indexService = new IndexService();
let index = await indexService.getIndexById(req.params.id);

const previousOwner = index.ownerDID.id.split(":").pop();
const newOwnerWallet = req.body.newOwner.split(":").pop();

const userAuthSig = getAuthSigFromDIDSession(req.session)
const vals = await transferOwnership({
userAuthSig: userAuthSig,
signerPublicKey: index?.signerPublicKey,
signerFunction: index.signerFunction,
previousOwner: previousOwner,
newOwner: newOwnerWallet,
});

if(vals){
const didService = new DIDService().setSession(req.session); //Personal
await didService.setDIDIndex(index.id, "owned", true);
await redis.hDel(`pkp:owner`, index.signerPublicKey);
}else{
return res.status(500).json({ error: "Unauthorized" });
}

return res.status(200).json(index);

} catch (error) {
res.status(500).json({ error: error.message });
}
};

export const deleteIndex = async (req, res, next) => {
try {

Expand Down
105 changes: 92 additions & 13 deletions api/src/libs/lit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { getResolver } from "key-did-resolver";
import { CID } from "multiformats/cid";
import { sendLit } from "./send_lit.js";
import {joinSignature} from "ethers/lib/utils.js";
import { IndexService } from "../../services/index.js";

const provider = new ethers.providers.JsonRpcProvider("https://chain-rpc.litprotocol.com/http", 175177);

Expand Down Expand Up @@ -105,19 +106,19 @@ class PKPSigner extends ethers.Signer {
if(params.jsParams.signList.signTransaction){
params.jsParams.signList.signTransaction.messageToSign = ethers.utils.arrayify(unsignedTxn);
}

/*
if(params.jsParams.signList.getPKPSession){
const didKeyBack = params.jsParams.signList.getPKPSession.didKey;
params.jsParams.signList.getPKPSession.didKey = params.jsParams.signList.getPKPSession.didKey.id.toString();
}
*/

const resp = await this.litNodeClient.executeJs(params);

if (!resp.signatures) {
throw new Error("No signature returned");
}

console.log("charlie", resp)
/*
if(resp.signatures.getPKPSession){
const { siweMessage } = JSON.parse(resp.response.context);
Expand All @@ -134,10 +135,8 @@ class PKPSigner extends ethers.Signer {
const did = await createDIDCacao(didKeyBack, cacao);
const pkpSession = new DIDSession({ cacao, keySeed, did });
if (sessionCacheKey) {
await redis.hSet("sessions", sessionCacheKey, pkpSession.serialize());
}
}
*/

if(resp.signatures.signTransaction){
const signature = resp.signatures.signTransaction.signature;
Expand Down Expand Up @@ -240,6 +239,81 @@ export const writeAuthMethods = async ({
};


export const transferOwnership = async ({
userAuthSig,
signerPublicKey,
signerFunction,
previousOwner,
newOwner,
}) => {
try {

const dAppSessionSigsResponse = await redis.get(
`lit:${config.litNetwork}:dAppSessionSigs`,
);
if (!dAppSessionSigsResponse) {
throw new Error("No session signatures found");
}
const dAppSessionSigs = JSON.parse(dAppSessionSigsResponse);
const signerFunctionV0 = CID.parse(signerFunction).toV0().toString();

if (!litNodeClient.ready) {
await litNodeClient.connect();
}
const from = ethers.utils.computeAddress(signerPublicKey).toLowerCase();

const signer = new PKPSigner(from, litNodeClient, provider , {
ipfsId: signerFunctionV0,
sessionSigs: dAppSessionSigs, // index app, which capacity credit, authorizes to pkp, not the user.
jsParams: {
authSig: userAuthSig, // for conditions control. to identify authenticated user.
publicKey: signerPublicKey,
chain: "ethereum", // polygon
nonce: randomString(12),
signList: {
signTransaction: {},
}
},
})

const litContracts = new LitContracts({
network: config.litNetwork,
signer: signer,
debug: false,
});

await litContracts.connect();

const pubKeyHash = ethers.utils.keccak256(signerPublicKey);
const tokenId = BigInt(pubKeyHash);

const transaction =
await litContracts.pkpPermissionsContract.write.batchAddRemoveAuthMethods(
tokenId,
[1],
[newOwner],
["0x"],
[[BigInt(1)]],
[1],
[previousOwner]
);

const res = await transaction.wait(3)

console.log("broadcast txn result:", JSON.stringify(transaction));
if(res){
return true;
}
return false;


} catch (error) {
console.error(error);
throw new Error("Error writing auth methods");
}
};


export const getOwner = async (pkpPubKey) => {

const litContracts = new LitContracts({
Expand All @@ -252,27 +326,31 @@ export const getOwner = async (pkpPubKey) => {
return existing;
}

const pubKeyHash = ethers.keccak256(pkpPubKey);
const pubKeyHash = ethers.utils.keccak256(pkpPubKey);
const pkpAddress = ethers.utils.computeAddress(pkpPubKey).toLowerCase();
const tokenId = BigInt(pubKeyHash);

if (!litContracts.connected) {
await litContracts.connect();
}

const address = await litContracts.pkpNftContract.read.ownerOf(tokenId);
const addressList = await litContracts.pkpPermissionsContractUtils.read.getPermittedAddresses(tokenId);
const address = addressList.filter(a => a.toLowerCase() !== pkpAddress.toLowerCase())[0]

await redis.hSet(`pkp:owner`, pkpPubKey, address);

return address;
};
export const getOwnerProfile = async (id) => {
export const getOwnerProfile = async (index) => {
const didService = new DIDService();

const owner = await didService.getOwner(id);
const owner = await didService.getOwner(index.id);
if (owner && owner.id) {
return owner;
} else {
return { id: `did:pkh:eip155:1:0` };
const ownerAddr = await getOwner(index.signerPublicKey);
return { id: `did:pkh:eip155:1:${ownerAddr}` };
}

};
export const encodeDIDWithLit = (pkpPubKey) => {
pkpPubKey = pkpPubKey.replace("0x", "");
Expand Down Expand Up @@ -391,11 +469,12 @@ export const getPKPSession = async (session, index) => {
if (!session.did.authenticated) {
throw new Error("Unauthenticated DID");
}
const owner = await getOwner(index.signerPublicKey);

let sessionCacheKey = false;

if (index.id && index.signerFunction) {
sessionCacheKey = `${session.did.parent}:${index.id}:${index.signerFunction}`;
sessionCacheKey = `${session.did.parent}:${owner}:${index.id}:${index.signerFunction}`;
const existingSessionStr = await redis.hGet("sessions", sessionCacheKey);
if (existingSessionStr) {
try {
Expand Down
16 changes: 16 additions & 0 deletions api/src/packages/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,22 @@ app.patch(
indexController.updateIndex,
);

app.patch(
"/indexes/:id/transfer",
authCheckMiddleware,
validator.body(
Joi.object({
newOwner: Joi.custom(isDID, "DID").required(),
}).or("newOwner"),
),
validator.params(
Joi.object({
id: Joi.custom(isStreamID, "Index ID").required(),
}),
),
indexController.transferIndex,
);

app.delete(
"/indexes/:id",
authCheckMiddleware,
Expand Down
4 changes: 2 additions & 2 deletions api/src/services/did.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class DIDService {
try {
const {data, errors} = await this.client.executeQuery(`
query{
dIDIndexIndex(first: 1, sorting: {createdAt: DESC}, filters: { where: {type: {equalTo: "owned"}, indexId: {equalTo: "${indexId}"}}}) {
dIDIndexIndex(first: 1, sorting: {createdAt: DESC}, filters: { where: {deletedAt: {isNull: true}, type: {equalTo: "owned"}, indexId: {equalTo: "${indexId}"}}}) {
edges {
node {
id
Expand Down Expand Up @@ -200,7 +200,7 @@ export class DIDService {
Object.values(indexes)
.filter(i => i.did.owned || i.did.starred)
.map(async (i) => {
const ownerDID = await getOwnerProfile(i.id);
const ownerDID = await getOwnerProfile(i);
return { ...i, ownerDID };
})
.sort((a, b) => {
Expand Down
2 changes: 1 addition & 1 deletion api/src/services/embedding.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class EmbeddingService {
throw new Error('Invalid response data');
}
try{
data.node.index.ownerDID = await getOwnerProfile(data.node.index.id);
data.node.index.ownerDID = await getOwnerProfile(data.node.index);
} catch(e) {
console.log("Error fetching profile", e)
}
Expand Down
6 changes: 3 additions & 3 deletions api/src/services/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export class IndexService {
index.did = did;
}

index.ownerDID = await getOwnerProfile(index.id);
index.ownerDID = await getOwnerProfile(index);



Expand Down Expand Up @@ -140,7 +140,7 @@ export class IndexService {

// Return the created index document
const createdIndex = data.createIndex.document;
createdIndex.ownerDID = await getOwnerProfile(createdIndex.id);
createdIndex.ownerDID = await getOwnerProfile(createdIndex);

return createdIndex;

Expand Down Expand Up @@ -226,7 +226,7 @@ export class IndexService {
index.did = did;
}

index.ownerDID = await getOwnerProfile(index.id);
index.ownerDID = await getOwnerProfile(index);
// Return the created index document
return index;

Expand Down

0 comments on commit 7dd95d2

Please sign in to comment.