| @@ -359,33 +359,105 @@ public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer | |||||
| * Used by consensus write phase, pre compute new block hash | * Used by consensus write phase, pre compute new block hash | ||||
| * | * | ||||
| */ | */ | ||||
| // public BatchAppResultImpl preComputeAppHash(byte[][] commands) { | |||||
| // String batchId = messageHandle.beginBatch(realmName); | |||||
| // List<AsyncFuture<byte[]>> asyncFutureLinkedList = new ArrayList<>(commands.length); | |||||
| // List<byte[]> responseLinkedList = new ArrayList<>(); | |||||
| // try { | |||||
| // int msgId = 0; | |||||
| // for (byte[] txContent : commands) { | |||||
| // AsyncFuture<byte[]> asyncFuture = messageHandle.processOrdered(msgId++, txContent, realmName, batchId); | |||||
| // asyncFutureLinkedList.add(asyncFuture); | |||||
| // } | |||||
| // StateSnapshot stateSnapshot = messageHandle.completeBatch(realmName, batchId); | |||||
| // byte[] blockHashBytes = stateSnapshot.getSnapshot(); | |||||
| // | |||||
| // for (int i = 0; i< asyncFutureLinkedList.size(); i++) { | |||||
| // responseLinkedList.add(asyncFutureLinkedList.get(i).get()); | |||||
| // } | |||||
| // | |||||
| // | |||||
| // return new BatchAppResultImpl(responseLinkedList, blockHashBytes, batchId); | |||||
| // | |||||
| // } catch (Exception e) { | |||||
| // // todo 需要处理应答码 404 | |||||
| // LOGGER.error("Error occurred while processing ordered messages! --" + e.getMessage(), e); | |||||
| // messageHandle.rollbackBatch(realmName, batchId, TransactionState.IGNORED_BY_CONSENSUS_PHASE_PRECOMPUTE_ROLLBACK.CODE); | |||||
| // } | |||||
| // | |||||
| // return null; | |||||
| // } | |||||
| /** | |||||
| * Used by consensus write phase, pre compute new block hash | |||||
| */ | |||||
| public BatchAppResultImpl preComputeAppHash(byte[][] commands) { | public BatchAppResultImpl preComputeAppHash(byte[][] commands) { | ||||
| String batchId = messageHandle.beginBatch(realmName); | String batchId = messageHandle.beginBatch(realmName); | ||||
| List<AsyncFuture<byte[]>> asyncFutureLinkedList = new ArrayList<>(commands.length); | List<AsyncFuture<byte[]>> asyncFutureLinkedList = new ArrayList<>(commands.length); | ||||
| List<byte[]> responseLinkedList = new ArrayList<>(); | List<byte[]> responseLinkedList = new ArrayList<>(); | ||||
| BatchAppResultImpl result; | |||||
| try { | try { | ||||
| int msgId = 0; | int msgId = 0; | ||||
| for (byte[] txContent : commands) { | |||||
| AsyncFuture<byte[]> asyncFuture = messageHandle.processOrdered(msgId++, txContent, realmName, batchId); | |||||
| asyncFutureLinkedList.add(asyncFuture); | |||||
| } | |||||
| StateSnapshot stateSnapshot = messageHandle.completeBatch(realmName, batchId); | |||||
| byte[] blockHashBytes = stateSnapshot.getSnapshot(); | |||||
| for (int i = 0; i< asyncFutureLinkedList.size(); i++) { | |||||
| responseLinkedList.add(asyncFutureLinkedList.get(i).get()); | |||||
| boolean isOK = true; | |||||
| for (int i = 0; i < commands.length; i++) { | |||||
| byte[] txContent = commands[i]; | |||||
| try { | |||||
| AsyncFuture<byte[]> asyncFuture = messageHandle.processOrdered(msgId++, txContent, realmName, batchId); | |||||
| asyncFutureLinkedList.add(asyncFuture); | |||||
| } catch (BlockRollbackException e) { | |||||
| LOGGER.error("Error occurred while processing ordered messages! --" + e.getMessage(), e); | |||||
| isOK = false; | |||||
| break; | |||||
| } | |||||
| } | } | ||||
| if (isOK) { | |||||
| StateSnapshot stateSnapshot = messageHandle.completeBatch(realmName, batchId); | |||||
| byte[] blockHashBytes = stateSnapshot.getSnapshot(); | |||||
| for (int i = 0; i < asyncFutureLinkedList.size(); i++) { | |||||
| responseLinkedList.add(asyncFutureLinkedList.get(i).get()); | |||||
| } | |||||
| result = new BatchAppResultImpl(responseLinkedList, blockHashBytes, batchId); | |||||
| result.setErrorCode((byte) 0); | |||||
| return result; | |||||
| } else { | |||||
| for (int i = 0; i < commands.length; i++) { | |||||
| responseLinkedList.add(createAppResponse(commands[i])); | |||||
| } | |||||
| Random random = new Random(); | |||||
| byte[] rand = new byte[4]; | |||||
| random.nextBytes(rand); | |||||
| return new BatchAppResultImpl(responseLinkedList, blockHashBytes, batchId); | |||||
| result = new BatchAppResultImpl(responseLinkedList, rand, batchId); | |||||
| result.setErrorCode((byte) 1); | |||||
| return result; | |||||
| } | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| // todo 需要处理应答码 404 | |||||
| LOGGER.error("Error occurred while processing ordered messages! --" + e.getMessage(), e); | |||||
| messageHandle.rollbackBatch(realmName, batchId, TransactionState.IGNORED_BY_CONSENSUS_PHASE_PRECOMPUTE_ROLLBACK.CODE); | |||||
| LOGGER.error("Error occurred while genearte batch app result! --" + e.getMessage(), e); | |||||
| throw e; | |||||
| } | } | ||||
| } | |||||
| return null; | |||||
| public byte[] createAppResponse(byte[] command) { | |||||
| TransactionRequest txRequest = BinaryProtocol.decode(command); | |||||
| TxResponseMessage resp = new TxResponseMessage(txRequest.getTransactionContent().getHash()); | |||||
| resp.setExecutionState(TransactionState.IGNORED_BY_BLOCK_FULL_ROLLBACK); | |||||
| return BinaryProtocol.encode(resp, TransactionResponse.class); | |||||
| } | } | ||||
| /** | /** | ||||
| @@ -5,15 +5,26 @@ import com.jd.blockchain.gateway.PeerService; | |||||
| import com.jd.blockchain.ledger.ContractCodeDeployOperation; | import com.jd.blockchain.ledger.ContractCodeDeployOperation; | ||||
| import com.jd.blockchain.ledger.Operation; | import com.jd.blockchain.ledger.Operation; | ||||
| import com.jd.blockchain.ledger.TransactionRequest; | import com.jd.blockchain.ledger.TransactionRequest; | ||||
| import org.apache.commons.io.FileUtils; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
| import org.springframework.util.ResourceUtils; | |||||
| import java.io.File; | |||||
| import java.net.URL; | |||||
| @Service | @Service | ||||
| public class GatewayInterceptServiceHandler implements GatewayInterceptService { | public class GatewayInterceptServiceHandler implements GatewayInterceptService { | ||||
| private static String contractsPath; | |||||
| @Autowired | @Autowired | ||||
| private PeerService peerService; | private PeerService peerService; | ||||
| static { | |||||
| contractsPath = jarRootDir(); | |||||
| } | |||||
| @Override | @Override | ||||
| public void intercept(TransactionRequest txRequest) { | public void intercept(TransactionRequest txRequest) { | ||||
| // 当前仅处理合约发布的请求 | // 当前仅处理合约发布的请求 | ||||
| @@ -29,7 +40,34 @@ public class GatewayInterceptServiceHandler implements GatewayInterceptService { | |||||
| } | } | ||||
| private void contractCheck(final ContractCodeDeployOperation contractOP) { | private void contractCheck(final ContractCodeDeployOperation contractOP) { | ||||
| // 校验chainCode | // 校验chainCode | ||||
| ContractJarUtils.verify(contractOP.getChainCode()); | ContractJarUtils.verify(contractOP.getChainCode()); | ||||
| } | } | ||||
| private static String jarRootDir() { | |||||
| try { | |||||
| URL url = GatewayInterceptServiceHandler.class.getProtectionDomain().getCodeSource().getLocation(); | |||||
| String currPath = java.net.URLDecoder.decode(url.getPath(), "UTF-8"); | |||||
| if (currPath.contains("!/")) { | |||||
| currPath = currPath.substring(5, currPath.indexOf("!/")); | |||||
| } | |||||
| if (currPath.endsWith(".jar")) { | |||||
| currPath = currPath.substring(0, currPath.lastIndexOf("/") + 1); | |||||
| } | |||||
| File file = new File(currPath); | |||||
| String homeDir = file.getParent(); | |||||
| String jarRootPath = homeDir + File.separator + "contracts"; | |||||
| FileUtils.forceMkdir(new File(jarRootPath)); | |||||
| return jarRootPath; | |||||
| } catch (Exception e) { | |||||
| throw new IllegalStateException(e); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -110,36 +110,46 @@ public class ContractJarUtils { | |||||
| return dotClassName; | return dotClassName; | ||||
| } | } | ||||
| public static void verify(byte[] chainCode) { | |||||
| public static void verify(String contractPath, byte[] chainCode) { | |||||
| if (chainCode == null || chainCode.length == 0) { | if (chainCode == null || chainCode.length == 0) { | ||||
| throw new IllegalStateException("Contract's chaincode is empty !!!"); | throw new IllegalStateException("Contract's chaincode is empty !!!"); | ||||
| } | } | ||||
| // 首先生成合约文件 | // 首先生成合约文件 | ||||
| File jarFile = newJarFile(); | |||||
| File jarFile = newJarTempFile(); | |||||
| try { | try { | ||||
| FileUtils.writeByteArrayToFile(jarFile, chainCode); | FileUtils.writeByteArrayToFile(jarFile, chainCode); | ||||
| // 校验合约文件 | // 校验合约文件 | ||||
| verify(jarFile); | |||||
| verify(contractPath, jarFile); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| throw new IllegalStateException(e); | throw new IllegalStateException(e); | ||||
| } finally { | } finally { | ||||
| // 删除文件 | // 删除文件 | ||||
| try { | try { | ||||
| FileUtils.forceDelete(jarFile); | |||||
| jarFile.deleteOnExit(); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| throw new IllegalStateException(e); | |||||
| // DO NOTHING | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| private static void verify(File jarFile) throws Exception { | |||||
| public static void verify(byte[] chainCode) { | |||||
| verify(null, chainCode); | |||||
| } | |||||
| private static void verify(String contractPath, File jarFile) throws Exception { | |||||
| // 首先判断jarFile中是否含有META-INF/JDCHAIN.TXT,并将其读出 | // 首先判断jarFile中是否含有META-INF/JDCHAIN.TXT,并将其读出 | ||||
| URL jarUrl = new URL("jar:file:" + jarFile.getPath() + "!/" + CONTRACT_MF); | URL jarUrl = new URL("jar:file:" + jarFile.getPath() + "!/" + CONTRACT_MF); | ||||
| InputStream inputStream = jarUrl.openStream(); | InputStream inputStream = jarUrl.openStream(); | ||||
| if (inputStream == null) { | if (inputStream == null) { | ||||
| throw new IllegalStateException(CONTRACT_MF + " IS NULL !!!"); | throw new IllegalStateException(CONTRACT_MF + " IS NULL !!!"); | ||||
| } | } | ||||
| byte[] bytes = IOUtils.toByteArray(inputStream); | |||||
| byte[] bytes; | |||||
| try { | |||||
| bytes = IOUtils.toByteArray(inputStream); | |||||
| } finally { | |||||
| inputStream.close(); | |||||
| } | |||||
| if (bytes == null || bytes.length == 0) { | if (bytes == null || bytes.length == 0) { | ||||
| throw new IllegalStateException(CONTRACT_MF + " IS Illegal !!!"); | throw new IllegalStateException(CONTRACT_MF + " IS Illegal !!!"); | ||||
| } | } | ||||
| @@ -147,20 +157,25 @@ public class ContractJarUtils { | |||||
| String txt = new String(bytes, StandardCharsets.UTF_8); | String txt = new String(bytes, StandardCharsets.UTF_8); | ||||
| // 生成新的Jar包文件,该文件路径与JarFile基本一致 | // 生成新的Jar包文件,该文件路径与JarFile基本一致 | ||||
| File tempJar = newJarFile(); | |||||
| // 复制除JDCHAIN.TXT之外的部分 | |||||
| copy(jarFile, tempJar, null, null, CONTRACT_MF); | |||||
| // 生成新Jar包对应的Hash内容 | |||||
| String verifyTxt = contractMF(FileUtils.readFileToByteArray(tempJar)); | |||||
| File tempJar = newJarTempFile(); | |||||
| try { | |||||
| // 复制除JDCHAIN.TXT之外的部分 | |||||
| copy(jarFile, tempJar, null, null, CONTRACT_MF); | |||||
| // 删除临时文件 | |||||
| FileUtils.forceDelete(tempJar); | |||||
| // 生成新Jar包对应的Hash内容 | |||||
| String verifyTxt = contractMF(FileUtils.readFileToByteArray(tempJar)); | |||||
| // 校验Jar包内容 | |||||
| if (!txt.equals(verifyTxt)) { | |||||
| throw new IllegalStateException(String.format("Jar [%s] verify Illegal !!!", jarFile.getName())); | |||||
| // 校验Jar包内容 | |||||
| if (!txt.equals(verifyTxt)) { | |||||
| throw new IllegalStateException(String.format("Jar [%s] verify Illegal !!!", jarFile.getName())); | |||||
| } | |||||
| } finally { | |||||
| try { | |||||
| // 删除临时文件 | |||||
| tempJar.deleteOnExit(); | |||||
| } catch (Exception e) { | |||||
| // DO NOTHING | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -218,11 +233,30 @@ public class ContractJarUtils { | |||||
| } | } | ||||
| } | } | ||||
| private static File newJarFile() { | |||||
| return new File("contract-" + | |||||
| System.currentTimeMillis() + "-" + | |||||
| System.nanoTime() + "-" + | |||||
| FILE_RANDOM.nextInt(1024) + | |||||
| ".jar"); | |||||
| private static File newJarTempFile() { | |||||
| try { | |||||
| return File.createTempFile("contract-" + | |||||
| System.currentTimeMillis() + "-" + | |||||
| System.nanoTime() + "-" + | |||||
| FILE_RANDOM.nextInt(1024), ".jar"); | |||||
| } catch (Exception e) { | |||||
| throw new IllegalStateException(e); | |||||
| } | |||||
| // | |||||
| // if (contractPath != null && contractPath.length() > 0) { | |||||
| // return new File(contractPath + File.separator + | |||||
| // "contract-" + | |||||
| // System.currentTimeMillis() + "-" + | |||||
| // System.nanoTime() + "-" + | |||||
| // FILE_RANDOM.nextInt(1024) + | |||||
| // ".jar"); | |||||
| // } | |||||
| // | |||||
| // return new File("contract-" + | |||||
| // System.currentTimeMillis() + "-" + | |||||
| // System.nanoTime() + "-" + | |||||
| // FILE_RANDOM.nextInt(1024) + | |||||
| // ".jar"); | |||||
| } | } | ||||
| } | } | ||||