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

add CrossChainOracle #236

Draft
wants to merge 25 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
35 changes: 30 additions & 5 deletions src/AuthorizationClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -799,12 +799,37 @@ class AuthorizationClient {
return Utils.FormatAddress(ownerAddress);
}

async SignDigest(message) {
if(!this.client.signer?.signDigest && !this.client.signer?._signingKey()?.signDigest) {
const signer = this.client.signer;
let signDigest;
if(signer.provider && signer.provider.request && signer.address) {
signDigest = (_message) => {
return signer.provider.request({
method: "personal_sign",
params: [signer.address, _message],
});
};
} else if(signer.provider && signer.provider.provider && signer.provider.provider.request && signer.address) {
signDigest = (_message) => {
return signer.provider.provider.request({
method: "personal_sign",
params: [signer.address, _message],
});
};
} else {
throw Error("SignDigest() not available; did you set a valid provider?");
}
return signDigest(message);
}

return this.client.signer.signDigest ?
await this.client.signer.signDigest(message) :
await this.client.signer._signingKey().signDigest(message);
}

async Sign(message) {
return await Ethers.utils.joinSignature(
this.client.signer.signDigest ?
await this.client.signer.signDigest(message) :
await this.client.signer._signingKey().signDigest(message)
);
return Ethers.utils.joinSignature(await this.SignDigest(message));
}

async KMSAddress({objectId, versionHash}) {
Expand Down
111 changes: 111 additions & 0 deletions src/CrossChainOracle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
const Utils = require("./Utils");
const {LogMessage} = require("./LogMessage");
const Pako = require("pako");

class CrossChainOracle {

constructor({client, contentSpaceId, debug=false}) {
this.client = client;
this.contentSpaceId = contentSpaceId;
this.debug = debug;
}

Log(...message) {
LogMessage(this, message, false);
}

/**
* Create a signed authorization token used for calling the cross-chain oracle.
* No need to call this externally, is called via this.XcoView().
*/
async CreateOracleAccessToken() {
const address = this.client.signer.address;
let token = {
sub: `iusr${Utils.AddressToHash(address)}`,
adr: Buffer.from(address.replace(/^0x/, ""), "hex").toString("base64"),
spc: this.contentSpaceId,
iat: Date.now(),
exp: Date.now() + 15 * 1000,
};

let message = `Eluvio Content Fabric Access Token 1.0\n${JSON.stringify(
token
)}`;

const signature = await this.client.signDigest(message);

const compressedToken = Pako.deflateRaw(
Buffer.from(JSON.stringify(token), "utf-8")
);
return `acspjc${Utils.B58(
Buffer.concat([
Buffer.from(signature.replace(/^0x/, ""), "hex"),
Buffer.from(compressedToken),
])
)}`;
}

/**
* Format a cross-chain oracle request.
* Used as the xcoRequest argument to this.XcoView().
*
* @methodGroup Authorization
* @namedParams
* @param {string=} chain_type - the chain type; "eip155", "flow", "solana"
* @param {string=} chain_id - the chain ID; a number for ethereum (eip155), "mainnet" or "testnet" for flow,
* - a hash "4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ" or "8E9rvCKLFQia2Y35HXjjpWzj8weVo44K" for solana
* @param {string=} asset_type - the asset type; "erc721" or "erc20" for an ethereum token or NFT,
* - "NonFungibleToken" for a flow or solana NFT.
* @param {string=} asset_id - the asset contract ID; a hex address "0x123.." for ethereum,
* - a hex:string - "0x...:NFT_NAME" for flow, or a base58 string for solana
*/
CreateXcoRequest({chain_type= "eip155", chain_id= "1", asset_type= "erc721", asset_id}) {
const xcoReq = {
chain_type: chain_type,
chain_id: chain_id,
asset_type: asset_type,
asset_id: asset_id,
method: "balance"
};
this.Log("xcoReq: ", xcoReq);
return xcoReq;
}

/**
* Make a cross-chain oracle call for the chain asset specified in the xcoRequest.
* Create the xcoRequest via this.CreateXcoRequest().
*
* @methodGroup Authorization
* @namedParams
* @param {object} xcoRequest - Object containing specification of the chain asset to query for token balance
*/
async XcoBalanceView({xcoRequest}) {
// Create a client-signed access token in order to access the cross-chain oracle API
const xcoToken = await this.CreateOracleAccessToken();
this.Log("oracle access token", xcoToken);
this.Log("oracle decoded token", this.client.utils.DecodeSignedToken(xcoToken));
this.Log("xcoRequest: ", xcoRequest);

// Call the cross-chain oracle 'view' API
try {
let res = await Utils.ResponseToFormat(
"json",
this.client.authClient.MakeAuthServiceRequest({
method: "POST",
path: "/as/xco/view",
body: xcoRequest,
headers: {
Authorization: `Bearer ${xcoToken}`,
},
})
);
this.Log("xcoResponse", res);
return res;
} catch(err) {
this.Log("XcoView error: ", err);
return null;
}
}
}

module.exports = CrossChainOracle;
41 changes: 41 additions & 0 deletions src/ElvClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const EthClient = require("./EthClient");
const UserProfileClient = require("./UserProfileClient");
const HttpClient = require("./HttpClient");
const RemoteSigner = require("./RemoteSigner");
const CrossChainOracle = require("./CrossChainOracle");

// const ContentObjectVerification = require("./ContentObjectVerification");
const Utils = require("./Utils");
Expand Down Expand Up @@ -420,6 +421,12 @@ class ElvClient {
debug: this.debug
});

this.crossChainOracle = new CrossChainOracle({
client: this,
contentSpaceId: this.contentSpaceId,
debug: this.debug
});

// Initialize crypto wasm
this.Crypto = Crypto;
this.Crypto.ElvCrypto();
Expand Down Expand Up @@ -679,9 +686,42 @@ class ElvClient {
ethProvider.pollingInterval = 250;
this.signer = ethProvider.getSigner();
this.signer.address = await this.signer.getAddress();
await this.SetSignDigest({provider});
await this.InitializeClients();
}

/**
* Set the client SignDigest method from an existing web3 provider.
* Called as part of SetSignerFromWeb3Provider.
*
* @methodGroup Signers
* @namedParams
* @param {object} provider - The web3 provider object
*/
async SetSignDigest({provider}) {
const userAddress = await this.signer.getAddress();
let signDigest;
if(provider && provider.request) {
signDigest = (_message) => {
return provider.request({
method: "personal_sign",
params: [userAddress, _message],
});
};
} else if(provider && provider.provider && provider.provider.request) {
signDigest = (_message) => {
return provider.provider.request({
method: "personal_sign",
params: [userAddress, _message],
});
};
} else {
this.Log("ERROR: cannot find a signDigest from provider, will use existing default", true);
}
this.signDigest = signDigest;
this.signer.signDigest = signDigest;
}

/**
* Initialize a new account using the provided funding and group tokens.
*
Expand Down Expand Up @@ -1107,6 +1147,7 @@ class ElvClient {
"SetRemoteSigner",
"SetSigner",
"SetSignerFromWeb3Provider",
"SetSignDigestFromWeb3Provider",
"Sign",
"ToggleLogging"
];
Expand Down
9 changes: 9 additions & 0 deletions utilities/ObjectGetMetadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ class ObjectGetMetadata extends Utility {
}
}

// avoid fetch ExperimentalWarnings
const originalEmit = process.emit;
process.emit = function (name, data, ...args) {
if(name === `warning` && typeof data === `object` && data.name === `ExperimentalWarning`) {
return false;
}
return originalEmit.apply(process, arguments);
};

if(require.main === module) {
Utility.cmdLineInvoke(ObjectGetMetadata);
} else {
Expand Down
9 changes: 9 additions & 0 deletions utilities/SimpleIngest.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,15 @@ class SimpleIngest extends Utility {
}
}

// avoid fetch ExperimentalWarnings
const originalEmit = process.emit;
process.emit = function (name, data, ...args) {
if(name === `warning` && typeof data === `object` && data.name === `ExperimentalWarning`) {
return false;
}
return originalEmit.apply(process, arguments);
};

if(require.main === module) {
Utility.cmdLineInvoke(SimpleIngest);
} else {
Expand Down