Java tutorial
package io.ucoin.ucoinj.core.client.service.bma; /* * #%L * UCoin Java :: Core Client API * %% * Copyright (C) 2014 - 2016 EIS * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import io.ucoin.ucoinj.core.client.model.ModelUtils; import io.ucoin.ucoinj.core.client.model.bma.WotCertification; import io.ucoin.ucoinj.core.client.model.bma.BlockchainBlock; import io.ucoin.ucoinj.core.client.model.bma.BlockchainParameters; import io.ucoin.ucoinj.core.client.model.bma.WotLookup; import io.ucoin.ucoinj.core.client.model.local.Certification; import io.ucoin.ucoinj.core.client.model.local.Identity; import io.ucoin.ucoinj.core.client.model.local.Peer; import io.ucoin.ucoinj.core.client.model.local.Wallet; import io.ucoin.ucoinj.core.client.service.ServiceLocator; import io.ucoin.ucoinj.core.exception.TechnicalException; import io.ucoin.ucoinj.core.service.CryptoService; import io.ucoin.ucoinj.core.util.CollectionUtils; import io.ucoin.ucoinj.core.util.ObjectUtils; import io.ucoin.ucoinj.core.util.crypto.CryptoUtils; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.message.BasicNameValuePair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.UnsupportedEncodingException; import java.util.*; public class WotRemoteServiceImpl extends BaseRemoteServiceImpl implements WotRemoteService { private static final Logger log = LoggerFactory.getLogger(BlockchainRemoteServiceImpl.class); public static final String URL_BASE = "/wot"; public static final String URL_ADD = URL_BASE + "/add"; public static final String URL_LOOKUP = URL_BASE + "/lookup/%s"; public static final String URL_REQUIREMENT = URL_BASE + "/requirements/%s"; public static final String URL_CERTIFIED_BY = URL_BASE + "/certified-by/%s"; public static final String URL_CERTIFIERS_OF = URL_BASE + "/certifiers-of/%s"; /** * See https://github.com/ucoin-io/ucoin-cli/blob/master/bin/ucoin * > var hash = res.current ? res.current.hash : 'DA39A3EE5E6B4B0D3255BFEF95601890AFD80709'; */ public static final String BLOCK_ZERO_HASH = "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709"; private CryptoService cryptoService; private BlockchainRemoteService bcService; public WotRemoteServiceImpl() { super(); } @Override public void afterPropertiesSet() { super.afterPropertiesSet(); cryptoService = ServiceLocator.instance().getCryptoService(); bcService = ServiceLocator.instance().getBlockchainRemoteService(); } public List<Identity> findIdentities(Set<Long> currenciesIds, String uidOrPubKey) { List<Identity> result = new ArrayList<Identity>(); String path = String.format(URL_LOOKUP, uidOrPubKey); for (Long currencyId : currenciesIds) { WotLookup lookupResult = executeRequest(currencyId, path, WotLookup.class); addAllIdentities(result, lookupResult, currencyId); } return result; } public WotLookup.Uid find(long currencyId, String uidOrPubKey) { if (log.isDebugEnabled()) { log.debug(String.format("Try to find user by looking up on [%s]", uidOrPubKey)); } // get parameter String path = String.format(URL_LOOKUP, uidOrPubKey); WotLookup lookupResults = executeRequest(currencyId, path, WotLookup.class); for (WotLookup.Result result : lookupResults.getResults()) { if (CollectionUtils.isNotEmpty(result.getUids())) { for (WotLookup.Uid uid : result.getUids()) { return uid; } } } return null; } public void getRequirments(long currencyId, String pubKey) { if (log.isDebugEnabled()) { log.debug(String.format("Try to find user requirements on [%s]", pubKey)); } // get parameter String path = String.format(URL_REQUIREMENT, pubKey); // TODO : managed requirements //WotLookup lookupResults = executeRequest(currencyId, path, WotLookup.class); } public WotLookup.Uid findByUid(long currencyId, String uid) { if (log.isDebugEnabled()) { log.debug(String.format("Try to find user info by uid: %s", uid)); } // call lookup String path = String.format(URL_LOOKUP, uid); WotLookup lookupResults = executeRequest(currencyId, path, WotLookup.class); // Retrieve the exact uid WotLookup.Uid uniqueResult = getUid(lookupResults, uid); if (uniqueResult == null) { return null; } return uniqueResult; } public WotLookup.Uid findByUidAndPublicKey(long currencyId, String uid, String pubKey) { if (log.isDebugEnabled()) { log.debug(String.format("Try to find user info by uid [%s] and pubKey [%s]", uid, pubKey)); } // call lookup String path = String.format(URL_LOOKUP, uid); WotLookup lookupResults = executeRequest(currencyId, path, WotLookup.class); // Retrieve the exact uid WotLookup.Uid uniqueResult = getUidByUidAndPublicKey(lookupResults, uid, pubKey); if (uniqueResult == null) { return null; } return uniqueResult; } public WotLookup.Uid findByUidAndPublicKey(Peer peer, String uid, String pubKey) { if (log.isDebugEnabled()) { log.debug(String.format("Try to find user info by uid [%s] and pubKey [%s]", uid, pubKey)); } // call lookup String path = String.format(URL_LOOKUP, uid); WotLookup lookupResults = executeRequest(peer, path, WotLookup.class); // Retrieve the exact uid WotLookup.Uid uniqueResult = getUidByUidAndPublicKey(lookupResults, uid, pubKey); if (uniqueResult == null) { return null; } return uniqueResult; } public Identity getIdentity(long currencyId, String uid, String pubKey) { if (log.isDebugEnabled()) { log.debug(String.format("Get identity by uid [%s] and pubKey [%s]", uid, pubKey)); } WotLookup.Uid lookupUid = findByUidAndPublicKey(currencyId, uid, pubKey); if (lookupUid == null) { return null; } return toIdentity(lookupUid); } public Identity getIdentity(long currencyId, String pubKey) { // Log.d(TAG, String.format("Get identity by uid [%s] and pubKey [%s]", uid, pubKey)); WotLookup.Uid lookupUid = find(currencyId, pubKey); if (lookupUid == null) { return null; } Identity result = toIdentity(lookupUid); result.setPubkey(pubKey); result.setCurrencyId(currencyId); return result; } public Identity getIdentity(Peer peer, String uid, String pubKey) { if (log.isDebugEnabled()) { log.debug(String.format("Get identity by uid [%s] and pubKey [%s]", uid, pubKey)); } WotLookup.Uid lookupUid = findByUidAndPublicKey(peer, uid, pubKey); if (lookupUid == null) { return null; } return toIdentity(lookupUid); } public Collection<Certification> getCertifications(long currencyId, String uid, String pubkey, boolean isMember) { ObjectUtils.checkNotNull(uid); ObjectUtils.checkNotNull(pubkey); if (isMember) { return getCertificationsByPubkeyForMember(currencyId, pubkey, true); } else { return getCertificationsByPubkeyForNonMember(currencyId, uid, pubkey); } } public WotCertification getCertifiedBy(long currencyId, String uid) { if (log.isDebugEnabled()) { log.debug(String.format("Try to get certifications done by uid: %s", uid)); } // call certified-by String path = String.format(URL_CERTIFIED_BY, uid); WotCertification result = executeRequest(currencyId, path, WotCertification.class); return result; } public int countValidCertifiers(long currencyId, String pubkey) { if (log.isDebugEnabled()) { log.debug(String.format("Try to count valid certifications done by pubkey: %s", pubkey)); } int count = 0; // call certified-by Collection<Certification> certifiersOf = getCertificationsByPubkeyForMember(currencyId, pubkey, false/*only certifiers of*/); if (CollectionUtils.isEmpty(certifiersOf)) { return 0; } for (Certification certifier : certifiersOf) { if (certifier.isValid()) { count++; } } return count; } public WotCertification getCertifiersOf(long currencyId, String uid) { if (log.isDebugEnabled()) { log.debug(String.format("Try to get certifications done to uid: %s", uid)); } // call certifiers-of String path = String.format(URL_CERTIFIERS_OF, uid); WotCertification result = executeRequest(currencyId, path, WotCertification.class); return result; } public void sendSelf(long currencyId, byte[] pubKey, byte[] secKey, String uid, long timestamp) { // http post /wot/add HttpPost httpPost = new HttpPost(getPath(currencyId, URL_ADD)); // Compute the pub key hash String pubKeyHash = CryptoUtils.encodeBase58(pubKey); // compute the self-certification String selfCertification = getSelfCertification(secKey, uid, timestamp); List<NameValuePair> urlParameters = new ArrayList<NameValuePair>(); urlParameters.add(new BasicNameValuePair("pubkey", pubKeyHash)); urlParameters.add(new BasicNameValuePair("self", selfCertification)); urlParameters.add(new BasicNameValuePair("other", "")); try { httpPost.setEntity(new UrlEncodedFormEntity(urlParameters)); } catch (UnsupportedEncodingException e) { throw new TechnicalException(e); } // Execute the request executeRequest(httpPost, String.class); } public String sendCertification(Wallet wallet, Identity identity) { return sendCertification(wallet.getCurrencyId(), wallet.getPubKey(), wallet.getSecKey(), wallet.getIdentity().getUid(), wallet.getIdentity().getTimestamp(), identity.getUid(), identity.getPubkey(), identity.getTimestamp(), identity.getSignature()); } public String sendCertification(long currencyId, byte[] pubKey, byte[] secKey, String uid, long timestamp, String userUid, String userPubKeyHash, long userTimestamp, String userSignature) { // http post /wot/add HttpPost httpPost = new HttpPost(getPath(currencyId, URL_ADD)); // Read the current block (number and hash) BlockchainRemoteService blockchainService = ServiceLocator.instance().getBlockchainRemoteService(); BlockchainBlock currentBlock = blockchainService.getCurrentBlock(currencyId); int blockNumber = currentBlock.getNumber(); String blockHash = (blockNumber != 0) ? currentBlock.getHash() : BLOCK_ZERO_HASH; // Compute the pub key hash String pubKeyHash = CryptoUtils.encodeBase58(pubKey); // compute the self-certification String selfCertification = getSelfCertification(userUid, userTimestamp, userSignature); // Compute the certification String certification = getCertification(pubKey, secKey, userUid, userTimestamp, userSignature, blockNumber, blockHash); String inlineCertification = toInlineCertification(pubKeyHash, userPubKeyHash, certification); List<NameValuePair> urlParameters = new ArrayList<NameValuePair>(); urlParameters.add(new BasicNameValuePair("pubkey", userPubKeyHash)); urlParameters.add(new BasicNameValuePair("self", selfCertification)); urlParameters.add(new BasicNameValuePair("other", inlineCertification)); try { httpPost.setEntity(new UrlEncodedFormEntity(urlParameters)); } catch (UnsupportedEncodingException e) { throw new TechnicalException(e); } String selfResult = executeRequest(httpPost, String.class); log.debug("received from /add: " + selfResult); return selfResult; } public void addAllIdentities(List<Identity> result, WotLookup lookupResults, Long currencyId) { String currencyName = null; if (currencyId != null) { currencyName = ServiceLocator.instance().getCurrencyService().getCurrencyNameById(currencyId); } for (WotLookup.Result lookupResult : lookupResults.getResults()) { String pubKey = lookupResult.getPubkey(); for (WotLookup.Uid source : lookupResult.getUids()) { // Create and fill an identity, from a result row Identity target = new Identity(); toIdentity(source, target); // fill the pub key target.setPubkey(pubKey); // Fill currency id and name // TODO //target.setCurrencyId(currencyId); //target.setCurrency(currencyName); result.add(target); } } } public Identity toIdentity(WotLookup.Uid source) { Identity target = new Identity(); toIdentity(source, target); return target; } public void toIdentity(WotLookup.Uid source, Identity target) { target.setUid(source.getUid()); target.setSelf(source.getSelf()); Long timestamp = source.getMeta() != null ? source.getMeta().timestamp : null; target.setTimestamp(timestamp); } public String getSelfCertification(byte[] secKey, String uid, long timestamp) { // Create the self part to sign StringBuilder buffer = new StringBuilder().append("UID:").append(uid).append("\nMETA:TS:").append(timestamp) .append('\n'); // Compute the signature String signature = cryptoService.sign(buffer.toString(), secKey); // Append the signature return buffer.append(signature).append('\n').toString(); } /* -- Internal methods -- */ protected Collection<Certification> getCertificationsByPubkeyForMember(long currencyId, String pubkey, boolean onlyCertifiersOf) { BlockchainParameters bcParameter = bcService.getParameters(currencyId, true); BlockchainBlock currentBlock = bcService.getCurrentBlock(currencyId, true); long medianTime = currentBlock.getMedianTime(); int sigValidity = bcParameter.getSigValidity(); int sigQty = bcParameter.getSigQty(); Collection<Certification> result = new TreeSet<Certification>( ModelUtils.newWotCertificationComparatorByUid()); // Certifiers of WotCertification certifiersOfList = getCertifiersOf(currencyId, pubkey); boolean certifiersOfIsEmpty = (certifiersOfList == null || certifiersOfList.getCertifications() == null); int validWrittenCertifiersCount = 0; if (!certifiersOfIsEmpty) { for (WotCertification.Certification certifier : certifiersOfList.getCertifications()) { Certification cert = toCertification(certifier, currencyId); cert.setCertifiedBy(false); result.add(cert); long certificationAge = medianTime - certifier.getTimestamp(); if (certificationAge <= sigValidity) { if (certifier.getIsMember() != null && certifier.getIsMember().booleanValue() && certifier.getWritten() != null && certifier.getWritten().getNumber() >= 0) { validWrittenCertifiersCount++; } cert.setValid(true); } else { cert.setValid(false); } } } if (validWrittenCertifiersCount >= sigQty) { if (log.isDebugEnabled()) { log.debug(String.format("pubkey [%s] has %s valid signatures: should be a member", pubkey, validWrittenCertifiersCount)); } } else { if (log.isDebugEnabled()) { log.debug(String.format("pubkey [%s] has %s valid signatures: not a member", pubkey, validWrittenCertifiersCount)); } } if (!onlyCertifiersOf) { // Certified by WotCertification certifiedByList = getCertifiedBy(currencyId, pubkey); boolean certifiedByIsEmpty = (certifiedByList == null || certifiedByList.getCertifications() == null); if (!certifiedByIsEmpty) { for (WotCertification.Certification certifiedBy : certifiedByList.getCertifications()) { Certification cert = toCertification(certifiedBy, currencyId); cert.setCertifiedBy(true); result.add(cert); long certificationAge = medianTime - certifiedBy.getTimestamp(); if (certificationAge <= sigValidity) { cert.setValid(true); } else { cert.setValid(false); } } } // Group certifications by [uid, pubKey] and keep last timestamp result = groupByUidAndPubKey(result, true); } return result; } protected Collection<Certification> getCertificationsByPubkeyForNonMember(long currencyId, final String uid, final String pubkey) { // Ordered list, by uid/pubkey/cert time Collection<Certification> result = new TreeSet<Certification>( ModelUtils.newWotCertificationComparatorByUid()); if (log.isDebugEnabled()) { log.debug(String.format("Get non member WOT, by uid [%s] and pubKey [%s]", uid, pubkey)); } // call lookup String path = String.format(URL_LOOKUP, pubkey); WotLookup lookupResults = executeRequest(currencyId, path, WotLookup.class); // Retrieve the exact uid WotLookup.Uid lookupUId = getUidByUidAndPublicKey(lookupResults, uid, pubkey); // Read certifiers, if any Map<String, Certification> certifierByPubkeys = new HashMap<String, Certification>(); if (lookupUId != null && lookupUId.getOthers() != null) { for (WotLookup.OtherSignature lookupSignature : lookupUId.getOthers()) { Collection<Certification> certifiers = toCertifierCertifications(lookupSignature, currencyId); result.addAll(certifiers); } } // Read certified-by if (CollectionUtils.isNotEmpty(lookupResults.getResults())) { for (WotLookup.Result lookupResult : lookupResults.getResults()) { if (lookupResult.signed != null) { for (WotLookup.SignedSignature lookupSignature : lookupResult.signed) { Certification certifiedBy = toCertifiedByCerticication(lookupSignature); // Set the currency Id certifiedBy.setCurrencyId(currencyId); // If exists, link to other side certification String certifiedByPubkey = certifiedBy.getPubkey(); if (certifierByPubkeys.containsKey(certifiedByPubkey)) { Certification certified = certifierByPubkeys.get(certifiedByPubkey); certified.setOtherEnd(certifiedBy); } // If only a certifier, just add to the list else { result.add(certifiedBy); } } } } } // Group certifications by [uid, pubKey] and keep last timestamp result = groupByUidAndPubKey(result, true); return result; } protected String toInlineCertification(String pubKeyHash, String userPubKeyHash, String certification) { // Read the signature String[] parts = certification.split("\n"); if (parts.length != 5) { throw new TechnicalException("Bad certification document: " + certification); } String signature = parts[parts.length - 1]; // Read the block number parts = parts[parts.length - 2].split(":"); if (parts.length != 3) { throw new TechnicalException("Bad certification document: " + certification); } parts = parts[2].split("-"); if (parts.length != 2) { throw new TechnicalException("Bad certification document: " + certification); } String blockNumber = parts[0]; return new StringBuilder().append(pubKeyHash).append(':').append(userPubKeyHash).append(':') .append(blockNumber).append(':').append(signature).append('\n').toString(); } public String getCertification(byte[] pubKey, byte[] secKey, String userUid, long userTimestamp, String userSignature, int blockNumber, String blockHash) { // Create the self part to sign String unsignedCertification = getCertificationUnsigned(userUid, userTimestamp, userSignature, blockNumber, blockHash); // Compute the signature String signature = cryptoService.sign(unsignedCertification, secKey); // Append the signature return new StringBuilder().append(unsignedCertification).append(signature).append('\n').toString(); } protected String getCertificationUnsigned(String userUid, long userTimestamp, String userSignature, int blockNumber, String blockHash) { // Create the self part to sign return new StringBuilder().append("UID:").append(userUid).append("\nMETA:TS:").append(userTimestamp) .append('\n').append(userSignature).append("\nMETA:TS:").append(blockNumber).append('-') .append(blockHash).append('\n').toString(); } protected String getSelfCertification(String uid, long timestamp, String signature) { // Create the self part to sign return new StringBuilder().append("UID:").append(uid).append("\nMETA:TS:").append(timestamp).append('\n') .append(signature) // FIXME : in ucoin, no '\n' here - is it a bug ? //.append('\n') .toString(); } protected WotLookup.Uid getUid(WotLookup lookupResults, String filterUid) { if (lookupResults.getResults() == null || lookupResults.getResults().length == 0) { return null; } for (WotLookup.Result result : lookupResults.getResults()) { if (result.getUids() != null && result.getUids().length > 0) { for (WotLookup.Uid uid : result.getUids()) { if (filterUid.equals(uid.getUid())) { return uid; } } } } return null; } protected WotLookup.Uid getUidByUidAndPublicKey(WotLookup lookupResults, String filterUid, String filterPublicKey) { if (lookupResults.getResults() == null || lookupResults.getResults().length == 0) { return null; } for (WotLookup.Result result : lookupResults.getResults()) { if (filterPublicKey.equals(result.getPubkey())) { if (result.getUids() != null && result.getUids().length > 0) { for (WotLookup.Uid uid : result.getUids()) { if (filterUid.equals(uid.getUid())) { return uid; } } } break; } } return null; } private Certification toCertification(final WotCertification.Certification source, final long currencyId) { Certification target = new Certification(); target.setPubkey(source.getPubkey()); target.setUid(source.getUid()); target.setMember(source.getIsMember()); target.setCurrencyId(currencyId); return target; } private Collection<Certification> toCertifierCertifications(final WotLookup.OtherSignature source, final long currencyId) { List<Certification> result = new ArrayList<Certification>(); // If only one uid if (source.getUids().length == 1) { Certification target = new Certification(); // uid target.setUid(source.getUids()[0]); // certifier target.setCertifiedBy(false); // Pubkey target.setPubkey(source.getPubkey()); // Is member target.setMember(source.isMember()); // Set currency Id target.setCurrencyId(currencyId); result.add(target); } else { for (String uid : source.getUids()) { Certification target = new Certification(); // uid target.setUid(uid); // certified by target.setCertifiedBy(false); // Pubkey target.setPubkey(source.getPubkey()); // Is member target.setMember(source.isMember()); // Set currency Id target.setCurrencyId(currencyId); result.add(target); } } return result; } private Certification toCertifiedByCerticication(final WotLookup.SignedSignature source) { Certification target = new Certification(); // uid target.setUid(source.uid); // certifieb by target.setCertifiedBy(true); if (source.meta != null) { // timestamp Long timestamp = source.meta != null ? source.meta.timestamp : null; if (timestamp != null) { target.setTimestamp(timestamp.longValue()); } } // Pubkey target.setPubkey(source.pubkey); // Is member target.setMember(source.isMember); // add to result list return target; } /** * * @param orderedCertifications a list, ordered by uid, pubkey, timestamp (DESC) * @return */ private Collection<Certification> groupByUidAndPubKey(Collection<Certification> orderedCertifications, boolean orderResultByDate) { if (CollectionUtils.isEmpty(orderedCertifications)) { return orderedCertifications; } List<Certification> result = new ArrayList<>(); StringBuilder keyBuilder = new StringBuilder(); String previousIdentityKey = null; Certification previousCert = null; for (Certification cert : orderedCertifications) { String identityKey = keyBuilder.append(cert.getUid()).append("~~").append(cert.getPubkey()).toString(); boolean certifiedBy = cert.isCertifiedBy(); // Seems to be the same identity as previous entry if (identityKey.equals(previousIdentityKey)) { if (certifiedBy != previousCert.isCertifiedBy()) { // merge with existing other End (if exists) merge(cert, previousCert.getOtherEnd()); // previousCert = certifier, so keep it and link the current cert if (!certifiedBy) { previousCert.setOtherEnd(cert); } // previousCert = certified-by, so prefer the current cert else { cert.setOtherEnd(previousCert); previousCert = cert; } } // Merge else { merge(previousCert, cert); } } // if identity changed else { // So add the previous cert to result if (previousCert != null) { result.add(previousCert); } // And prepare next iteration previousIdentityKey = identityKey; previousCert = cert; } // prepare the next loop keyBuilder.setLength(0); } if (previousCert != null) { result.add(previousCert); } if (orderResultByDate) { Collections.sort(result, ModelUtils.newWotCertificationComparatorByDate()); } return result; } private void merge(Certification previousCert, Certification cert) { if (cert != null && cert.getTimestamp() > previousCert.getTimestamp()) { previousCert.setTimestamp(cert.getTimestamp()); } } }