@@ -359,33 +359,105 @@ public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer | |||
* 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) { | |||
String batchId = messageHandle.beginBatch(realmName); | |||
List<AsyncFuture<byte[]>> asyncFutureLinkedList = new ArrayList<>(commands.length); | |||
List<byte[]> responseLinkedList = new ArrayList<>(); | |||
BatchAppResultImpl result; | |||
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()); | |||
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) { | |||
// 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.Operation; | |||
import com.jd.blockchain.ledger.TransactionRequest; | |||
import org.apache.commons.io.FileUtils; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import org.springframework.util.ResourceUtils; | |||
import java.io.File; | |||
import java.net.URL; | |||
@Service | |||
public class GatewayInterceptServiceHandler implements GatewayInterceptService { | |||
private static String contractsPath; | |||
@Autowired | |||
private PeerService peerService; | |||
static { | |||
contractsPath = jarRootDir(); | |||
} | |||
@Override | |||
public void intercept(TransactionRequest txRequest) { | |||
// 当前仅处理合约发布的请求 | |||
@@ -29,7 +40,34 @@ public class GatewayInterceptServiceHandler implements GatewayInterceptService { | |||
} | |||
private void contractCheck(final ContractCodeDeployOperation contractOP) { | |||
// 校验chainCode | |||
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; | |||
} | |||
public static void verify(byte[] chainCode) { | |||
public static void verify(String contractPath, byte[] chainCode) { | |||
if (chainCode == null || chainCode.length == 0) { | |||
throw new IllegalStateException("Contract's chaincode is empty !!!"); | |||
} | |||
// 首先生成合约文件 | |||
File jarFile = newJarFile(); | |||
File jarFile = newJarTempFile(); | |||
try { | |||
FileUtils.writeByteArrayToFile(jarFile, chainCode); | |||
// 校验合约文件 | |||
verify(jarFile); | |||
verify(contractPath, jarFile); | |||
} catch (Exception e) { | |||
throw new IllegalStateException(e); | |||
} finally { | |||
// 删除文件 | |||
try { | |||
FileUtils.forceDelete(jarFile); | |||
jarFile.deleteOnExit(); | |||
} 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,并将其读出 | |||
URL jarUrl = new URL("jar:file:" + jarFile.getPath() + "!/" + CONTRACT_MF); | |||
InputStream inputStream = jarUrl.openStream(); | |||
if (inputStream == 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) { | |||
throw new IllegalStateException(CONTRACT_MF + " IS Illegal !!!"); | |||
} | |||
@@ -147,20 +157,25 @@ public class ContractJarUtils { | |||
String txt = new String(bytes, StandardCharsets.UTF_8); | |||
// 生成新的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"); | |||
} | |||
} |