Closed shohu closed 5 years ago
https://github.com/loucho/hyperledger-fabric/blob/master/fabcar/javascript-low-level/query.js#L23-L28 この辺りにhintが。
以下で実現できてるっぽい
var firstnetwork_path = path.resolve('..', '..', 'first-network');
var org1tlscacert_path = path.resolve(firstnetwork_path, 'crypto-config', 'peerOrganizations', 'org1.example.com', 'tlsca', 'tlsca.org1.example.com-cert.pem');
var org1tlscacert = fs.readFileSync(org1tlscacert_path, 'utf8');
:
// setup the fabric network
var channel = fabric_client.newChannel('mychannel');
var peer = fabric_client.newPeer('grpcs://localhost:7051', {
'ssl-target-name-override': 'peer0.org1.example.com',
pem: org1tlscacert
});
channel.addPeer(peer);
// queryCar chaincode function - requires 1 argument, ex: args: ['CAR4'],
// queryAllCars chaincode function - requires no arguments , ex: args: [''],
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryAllCars',
args: ['']
};
// send the query proposal to the peer
return channel.queryByChaincode(request);
ただ、以下のように実装しても
var request = {
fcn: 'find',
args: [
'apart',
'000001',
],
chaincodeId: 'estate'
};
let response_payloads = await channel.queryByChaincode(request);
以下エラー
Error: No identity has been assigned to this client
以下部分で "No identity has been assigned to this client" を出している。
_getSigningIdentity(admin) {
logger.debug('_getSigningIdentity - admin parameter is %s :%s', (typeof admin), admin);
if (admin && this._adminSigningIdentity) {
return this._adminSigningIdentity;
} else {
if (this._userContext) {
return this._userContext.getSigningIdentity();
} else {
throw new Error('No identity has been assigned to this client');
}
}
}
user.js
/**
* Get the {@link SigningIdentity} object for this User instance, used to generate signatures
* @returns {SigningIdentity} the identity object that encapsulates the user's private key for signing
*/
getSigningIdentity() {
return this._signingIdentity;
}
queryByChaincode
/**
* Sends a proposal to one or more endorsing peers that will be handled by the chaincode.
* There is no difference in how the endorsing peers process a request
* to invoke a chaincode for transaction vs. to invoke a chaincode for query. All requests
* will be presented to the target chaincode's 'Invoke' method which must be implemented to
* understand from the arguments that this is a query request. The chaincode must also return
* results in the byte array format and the caller will have to be able to decode
* these results.
*
* If the request contains a <code>txId</code> property, that transaction ID will be used, and its administrative
* privileges will apply. In this case the <code>useAdmin</code> parameter to this function will be ignored.
*
* @param {ChaincodeQueryRequest} request
* @param {boolean} useAdmin - Optional. Indicates that the admin credentials should be used in making
* this call. Ignored if the <code>request</code> contains a <code>txId</code> property.
* @returns {Promise} A Promise for an array of byte array results returned from the chaincode
* on all Endorsing Peers
* @example
* <caption>Get the list of query results returned by the chaincode</caption>
* const responsePayloads = await channel.queryByChaincode(request);
* for (let i = 0; i < responsePayloads.length; i++) {
* console.log(util.format('Query result from peer [%s]: %s', i, responsePayloads[i].toString('utf8')));
* }
*/
async queryByChaincode(request, useAdmin) {
logger.debug('queryByChaincode - start');
if (!request) {
throw new Error('Missing request object for this queryByChaincode call.');
}
if (request.txId) {
useAdmin = request.txId.isAdmin();
}
const targets = this._getTargets(request.targets, Constants.NetworkConfig.CHAINCODE_QUERY_ROLE);
const signer = this._clientContext._getSigningIdentity(useAdmin);
const txId = request.txId || new TransactionID(signer, useAdmin);
const proposalResults = await Channel.sendTransactionProposal(query_request, this._name, this._clientContext, request.request_timeout);
const responses = proposalResults[0];
logger.debug('queryByChaincode - results received');
if (!responses || !Array.isArray(responses)) {
throw new Error('Payload results are missing from the chaincode query');
}
const results = [];
responses.forEach((response) => {
if (response instanceof Error) {
results.push(response);
} else if (response.response && response.response.payload) {
if (response.response.status === 200) {
results.push(response.response.payload);
} else {
if (response.response.message) {
results.push(new Error(response.response.message));
} else {
results.push(new Error(response));
}
}
} else {
logger.error('queryByChaincode - unknown or missing results in query ::' + results);
results.push(new Error(response));
}
});
return results;
/*
* Internal static method to allow transaction proposals to be called without
* creating a new channel
*/
static async sendTransactionProposal(request, channelId, client_context, timeout) {
const method = 'sendTransactionProposal(static)';
logger.debug('%s - start', method);
let errorMsg = client_utils.checkProposalRequest(request, true);
if (errorMsg) {
// do nothing so we skip the rest of the checks
} else if (!request.args) {
// args is not optional because we need for transaction to execute
errorMsg = 'Missing "args" in Transaction proposal request';
} else if (!request.targets || request.targets.length < 1) {
errorMsg = 'Missing peer objects in Transaction proposal';
}
if (errorMsg) {
logger.error('%s error %s', method, errorMsg);
throw new Error(errorMsg);
}
const proposal = Channel._buildSignedProposal(request, channelId, client_context);
const responses = await client_utils.sendPeersProposal(request.targets, proposal.signed, timeout);
return [responses, proposal.source];
}
adminで実行してしまうとまずいので、_userContext があればいいと。 _userContext が設定されている箇所は以下
/**
* Sets an instance of the {@link User} class as the security context of this client instance. This user’s
* signing identity (the private key and its corresponding certificate), will be used to sign all requests
* with the fabric backend.
* <br><br>
* Upon setting the user context, the SDK saves the object in a persistence cache if the “state store”
* has been set on the Client instance. If no state store has been set, this cache will not be established
* and the application is responsible for setting the user context again if the application crashes and is recovered.
*
* @param {User | UserNamePasswordObject} user - An instance of the User class encapsulating the authenticated
* user’s signing materials (private key and enrollment certificate).
* The parameter may also be a {@link UserNamePasswordObject} that contains the username
* and optionally the password and caName. A common connection profile must has been loaded to use the
* {@link UserNamePasswordObject} which will also create the user context and set it on
* this client instance. The created user context will be based on the current network
* configuration( i.e. the current organization's CA, current persistence stores).
* @param {boolean} skipPersistence - Whether to skip saving the user object into persistence. Default is false and the
* method will attempt to save the user object to the state store. When using a
* common connection profile and {@link UserNamePasswordObject}, the user object will
* always be stored to the persistence store.
* @returns {Promise} Promise of the 'user' object upon successful persistence of the user to the state store
*/
async setUserContext(user, skipPersistence) {
logger.debug(`setUserContext - user: ${user}, skipPersistence: ${skipPersistence}`);
if (!user) {
logger.debug('setUserContext, Cannot save null userContext.');
throw new Error('Cannot save null userContext.');
}
if (user && user.constructor && user.constructor.name === 'User') {
this._userContext = user;
if (!skipPersistence) {
logger.debug('setUserContext - begin promise to saveUserToStateStore');
return this.saveUserToStateStore();
}
logger.debug('setUserContext - resolved user');
return user;
}
// must be they have passed in an object
logger.debug('setUserContext - will try to use common connection profile to set the user');
return this._setUserFromConfig(user);
}
以下作ってみたが相変わらず "Error: No identity has been assigned to this client"
const testclient = new Client();
var testchannel = testclient.newChannel('mychannel');
const peerTLSCertPath = path.resolve(
__dirname,
'../../first-network/crypto-config/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/msp/tlscacerts/tlsca.org1.example.com-cert.pem'
)
const peerPEMCert = fs.readFileSync(peerTLSCertPath, 'utf8')
const peer = testclient.newPeer('grpcs://localhost:7051', {
pem: peerPEMCert,
'ssl-target-name-override': 'peer0.org1.example.com'
})
testchannel.addPeer(peer);
const request = {
fcn: 'find',
args: [
'apart',
'000001',
],
chaincodeId: 'estate'
};
let response = await testchannel.queryByChaincode(request);
console.log('evaluteTest ====== ', response);
やはり、setUserContext を作る必要がある。 ただ、setUserContext には以下のように password 渡すことになるから、サーバー側にpassword渡ってしまう?
client.setUserContext({username:'user1', password:secret});
$ ./startFabric.sh javascript
$ cd javascript-low-level/
$ npm install
$ node enrollAdmin.js
Store path:/Users/shohu33/sc/estate-sample/fabcar/javascript-low-level/hfc-key-store
Successfully enrolled admin user "admin"
Assigned the admin user to the fabric client ::{"name":"admin","mspid":"Org1MSP","roles":null,"affiliation":"","enrollmentSecret":"","enrollment":{"signingIdentity":"7d25ef56083b2cb85cef53ae1352283b613ca19006953a81a17f9e8f3c3e0707","identity":{"certificate":"-----BEGIN CERTIFICATE-----\nMIICAjCCAaigAwIBAgIUGx1pRPdexcGhZ5GBUxsZzA6AKDcwCgYIKoZIzj0EAwIw\nczELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh\nbiBGcmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMT\nE2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMTkwNjE5MDE1NzAwWhcNMjAwNjE4MDIw\nMjAwWjAhMQ8wDQYDVQQLEwZjbGllbnQxDjAMBgNVBAMTBWFkbWluMFkwEwYHKoZI\nzj0CAQYIKoZIzj0DAQcDQgAE5wyP3hVtaFTYg0fx3IC8d33IDVpUEHw4WwWQO1le\nWrR9O/KxOn2kf5BtVKNPdGAYF2CfmK7+BvBSh8quhzbDjaNsMGowDgYDVR0PAQH/\nBAQDAgeAMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPPjMBDvuOOQe0Vl+GXxfLez\nTEBSMCsGA1UdIwQkMCKAIBe2+SNa4mfmXrND5Psf5M3vNpBSTZhMQXHBoyyJ8LMJ\nMAoGCCqGSM49BAMCA0gAMEUCIQCKHgLSe2uuBwsXjetoljQjbADSVAcrMOUlxUME\nJLwKPAIgYBKosJojR4awlXj7iAwOUpCQ2e1hl4gnq36Gf376Zqg=\n-----END CERTIFICATE-----\n"}}}
$ node registerUser.js
Store path:/Users/shohu33/sc/estate-sample/fabcar/javascript-low-level/hfc-key-store
Successfully loaded admin from persistence
Successfully registered user1 - secret:NmrTRXobGVVP
Successfully enrolled member user "user1"
User1 was successfully registered and enrolled and is ready to interact with the fabric network
$ node invoke.js
--- invoke.js - start
Setting up client side network objects
Created client side object to represent the channel
Created client side object to represent the peer
Setting up the user store at path:/Users/shohu33/sc/estate-sample/fabcar/javascript-low-level/hfc-key-store
Successfully loaded "user1" from user store
Successfully setup client side
Start invoke processing
Used service discovery to initialize the channel
Created a transaction ID: 93b98c97519fcdaa77ad20ddc270d0d6cdc80686abf8fd52d4c55868f1f94903
Successfully sent Proposal and received response: Status - 200
Registered transaction listener with the peer event service for transaction ID:93b98c97519fcdaa77ad20ddc270d0d6cdc80686abf8fd52d4c55868f1f94903
Sending endorsed transaction to the orderer
The transaction has been committed on peer localhost:7051
Successfully sent transaction to the orderer
Successfully committed the change to the ledger by the peer
- try running "node query.js" to see the results
--- invoke.js - end
同じ書き方で、以下のようにデータ取得できるんだよな、、、なぞ
$ node query.js
Store path:/Users/shohu33/sc/estate-sample/fabcar/javascript-low-level/hfc-key-store
Successfully loaded user1 from persistence
Query has completed, checking results
Response is "[{\"Key\":\"CAR0\",\"Record\":{\"color\":\"blue\",\"docType\":\"car\",\"make\":\"Toyota\",\"model\":\"Prius\",\"owner\":\"Tomoko\"}},{\"Key\":\"CAR1\",\"Record\":{\"color\":\"red\",\"docType\":\"car\",\"make\":\"Ford\",\"model\":\"Mustang\",\"owner\":\"Brad\"}},{\"Key\":\"CAR12\",\"Record\":{\"color\":\"Black\",\"docType\":\"car\",\"make\":\"Honda\",\"model\":\"Accord\",\"owner\":\"Tom\"}},{\"Key\":\"CAR2\",\"Record\":{\"color\":\"green\",\"docType\":\"car\",\"make\":\"Hyundai\",\"model\":\"Tucson\",\"owner\":\"Jin Soo\"}},{\"Key\":\"CAR3\",\"Record\":{\"color\":\"yellow\",\"docType\":\"car\",\"make\":\"Volkswagen\",\"model\":\"Passat\",\"owner\":\"Max\"}},{\"Key\":\"CAR4\",\"Record\":{\"color\":\"black\",\"docType\":\"car\",\"make\":\"Tesla\",\"model\":\"S\",\"owner\":\"Adriana\"}},{\"Key\":\"CAR5\",\"Record\":{\"color\":\"purple\",\"docType\":\"car\",\"make\":\"Peugeot\",\"model\":\"205\",\"owner\":\"Michel\"}},{\"Key\":\"CAR6\",\"Record\":{\"color\":\"white\",\"docType\":\"car\",\"make\":\"Chery\",\"model\":\"S22L\",\"owner\":\"Aarav\"}},{\"Key\":\"CAR7\",\"Record\":{\"color\":\"violet\",\"docType\":\"car\",\"make\":\"Fiat\",\"model\":\"Punto\",\"owner\":\"Pari\"}},{\"Key\":\"CAR8\",\"Record\":{\"color\":\"indigo\",\"docType\":\"car\",\"make\":\"Tata\",\"model\":\"Nano\",\"owner\":\"Valeria\"}},{\"Key\":\"CAR9\",\"Record\":{\"color\":\"brown\",\"docType\":\"car\",\"make\":\"Holden\",\"model\":\"Barina\",\"owner\":\"Shotaro\"}}]"
newDefaultKeyValueStoreとかで前処理やってるのか。これだ。
// create the key value store as defined in the fabric-client/config/default.json 'key-value-store' setting
Fabric_Client.newDefaultKeyValueStore({ path: store_path
}).then((state_store) => {
// assign the store to the fabric client
fabric_client.setStateStore(state_store);
var crypto_suite = Fabric_Client.newCryptoSuite();
// use the same location for the state store (where the users' certificate are kept)
// and the crypto store (where the users' keys are kept)
var crypto_store = Fabric_Client.newCryptoKeyStore({path: store_path});
crypto_suite.setCryptoKeyStore(crypto_store);
fabric_client.setCryptoSuite(crypto_suite);
// get the enrolled user from persistence, this user will sign all requests
return fabric_client.getUserContext('user1', true);
}).then((user_from_store) => {
if (user_from_store && user_from_store.isEnrolled()) {
console.log('Successfully loaded user1 from persistence');
} else {
throw new Error('Failed to get user1.... run registerUser.js');
}
// queryCar chaincode function - requires 1 argument, ex: args: ['CAR4'],
// queryAllCars chaincode function - requires no arguments , ex: args: [''],
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryAllCars',
args: ['']
};
// send the query proposal to the peer
return channel.queryByChaincode(request);
queryByChaincode は client. setUserContext つかわなければ access できなさそうで、これは secret をサーバーに通信しなければいけなさそうだから、offlineじゃないっぽい?
https://github.com/shohu/estate-sample/issues/11#issuecomment-503003305 でも書いたが、ledgerからデータだけ取得したい場合のofflineの方法がわからない。
を使った際のresponseが
とだけで、データが取得できない。
queryByChaincode は channelからtransaction発行するわけではなさそうなので、こちらを調べてみる