Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Sample EthereumJ transaction sending
package com.mypackage.ethereumsync;
import java.io.File;
import java.math.BigInteger;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.commons.codec.binary.Hex;
import org.ethereum.config.SystemProperties;
import org.ethereum.core.Transaction;
import org.ethereum.core.TransactionReceipt;
import org.ethereum.crypto.ECKey;
import org.ethereum.db.ByteArrayWrapper;
import org.ethereum.facade.Ethereum;
import org.ethereum.facade.EthereumFactory;
import org.ethereum.listener.EthereumListenerAdapter;
import org.ethereum.util.ByteUtil;
import org.ethereum.util.RLP;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import com.google.common.annotations.VisibleForTesting;
@Service
public class EthereumService {
private static final Logger logger = LoggerFactory.getLogger(EthereumService.class);
private Map<ByteArrayWrapper, TransactionReceipt> txWaiters =
Collections.synchronizedMap(new HashMap<ByteArrayWrapper, TransactionReceipt>());
/**
* We have two nodes so that we can send ETH from one to the other and vice versa
*/
private Ethereum ethereum1;
private Ethereum ethereum2;
private CountDownLatch readyLatch = new CountDownLatch(2);
private ExecutorService starter;
private Random random = new Random();
private Future<?> initFuture;
@PostConstruct
public void init() throws Exception {
starter = Executors.newSingleThreadExecutor();
initFuture = starter.submit(this::start);
}
@PreDestroy
public void destroy() {
starter.shutdown();
ethereum1.close();
ethereum2.close();
}
public void start() {
ethereum1 = EthereumFactory.createEthereum(EthereumConfig1.class);
ethereum1.addListener(new EthereumListener(this));
ethereum2 = EthereumFactory.createEthereum(EthereumConfig2.class);
ethereum2.addListener(new EthereumListener(this));
}
public Future<TransactionReceipt> storeHashInEthereum(String hash) {
try {
// both clients must be synced before we proceed with storing anything
if (EthereumConfig1.PROPERTIES.isSyncEnabled() && EthereumConfig2.PROPERTIES.isSyncEnabled()) {
readyLatch.await();
}
// we have 2 ethereum clients, and we choose randomly the sender and the receiver
SystemProperties senderProps;
SystemProperties receiverProps;
Ethereum sender;
if (random.nextBoolean()) {
logger.info("Selected client1 as sender");
sender = ethereum1;
senderProps = EthereumConfig1.PROPERTIES;
receiverProps = EthereumConfig2.PROPERTIES;
} else {
logger.info("Selected client2 as sender");
sender = ethereum2;
senderProps = EthereumConfig2.PROPERTIES;
receiverProps = EthereumConfig1.PROPERTIES;
}
byte[] senderKey = senderProps.getMyKey().getPrivKeyBytes();
byte[] receiverAddress = receiverProps.getMyKey().getAddress();
TransactionReceipt receipt = sendTxAndWait(senderKey, receiverAddress, Base64.getUrlDecoder().decode(hash), sender);
return AsyncResult.forValue(receipt);
} catch (Exception ex) {
logger.error("Failed to store hash in Ethereum", ex);
return AsyncResult.forExecutionException(ex);
}
}
private Future<TransactionReceipt> sendTxAndWait(byte[] senderPrivateKey, byte[] receiveAddress, byte[] data, Ethereum senderNode) throws InterruptedException {
byte[] fromAddress = ECKey.fromPrivate(senderPrivateKey).getAddress();
ether
BigInteger nonce = senderNode.getRepository().getNonce(fromAddress);
Integer chainId = senderNode.getChainIdForNextBlock();
Transaction tx = new Transaction(
ByteUtil.bigIntegerToBytes(nonce),
ByteUtil.longToBytesNoLeadZeroes(senderNode.getGasPrice()),
ByteUtil.longToBytesNoLeadZeroes(200000),
receiveAddress,
ByteUtil.bigIntegerToBytes(BigInteger.valueOf(1)), // 1 gwei
data,
chainId);
tx.sign(ECKey.fromPrivate(senderPrivateKey));
logger.info("Raw transaction: 0x{}", Hex.encodeHexString(tx.getEncoded()));
new Transaction(getEncodedSignedRaw(tx)).verify();
senderNode.submitTransaction(tx);
logger.info("<=== Sending transaction: " + tx);
return waitForTx(tx.getHash(), senderNode);
}
private TransactionReceipt waitForTx(byte[] txHash, Ethereum senderNode) throws InterruptedException {
ByteArrayWrapper txHashW = new ByteArrayWrapper(txHash);
txWaiters.put(txHashW, null);
long startBlock = senderNode.getBlockchain().getBestBlock().getNumber();
while(true) {
TransactionReceipt receipt = txWaiters.get(txHashW);
if (receipt != null) {
return receipt;
} else {
long curBlock = senderNode.getBlockchain().getBestBlock().getNumber();
if (curBlock > startBlock + 16) {
throw new RuntimeException("The transaction was not included during last 16 blocks: " + txHashW.toString().substring(0,8));
} else {
logger.info("Waiting for block with transaction 0x" + txHashW.toString().substring(0,8) +
" included (" + (curBlock - startBlock) + " blocks received so far) ...");
}
}
synchronized (txHashW) {
txHashW.wait(20000);
}
}
}
public Ethereum getEthereum1() {
return ethereum1;
}
public Ethereum getEthereum2() {
return ethereum2;
}
@VisibleForTesting
public void awaitInitialization() throws InterruptedException, ExecutionException {
initFuture.get();
}
public static class EthereumListener extends EthereumListenerAdapter {
private EthereumService service;
public EthereumListener(EthereumService ethereumService) {
this.service = ethereumService;
}
@Override
public void onSyncDone(SyncState state) {
logger.info("Sync done: state={}", state);
service.readyLatch.countDown();
}
}
/**
* Configuring two clients requires separate configuration classes with the same functionality
*
* @author bozho
*
*/
@Configuration
@Conditional(value=ConfigurationRegistrationCondition.class)
@PropertySource("classpath:/ethereumsync.properties")
public static class EthereumConfig1 {
public static SystemProperties PROPERTIES;
@Value("${ethereumj.config.path}")
private String configPath;
@Bean
public SystemProperties systemProperties() {
if (PROPERTIES == null) {
PROPERTIES = new SystemProperties(new File(configPath + "/user1.conf"));
logger.info("Address for client1: 0x{}", Hex.encodeHexString(PROPERTIES.getMyKey().getAddress()));
}
return PROPERTIES;
}
}
@Configuration
@Conditional(value=ConfigurationRegistrationCondition.class)
@PropertySource("classpath:/ethereumsync.properties")
public static class EthereumConfig2 {
public static SystemProperties PROPERTIES;
@Value("${ethereumj.config.path}")
private String configPath;
@Bean
public SystemProperties systemProperties() {
if (PROPERTIES == null) {
PROPERTIES = new SystemProperties(new File(configPath + "/user2.conf"));
logger.info("Address for client2: 0x{}", Hex.encodeHexString(PROPERTIES.getMyKey().getAddress()));
}
return PROPERTIES;
}
}
// needed in order to not register EthereumConfigs with the main context, just with the nested ethereumj ones
public static class ConfigurationRegistrationCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getRegistry().containsBeanDefinition("ethereumService")) {
return false;
}
return true;
}
}
}
@ciscolxh

This comment has been minimized.

Copy link

@ciscolxh ciscolxh commented Mar 13, 2018

new Transaction(getEncodedSignedRaw(tx)).verify();
What's the use of this line of code?

@straw1105

This comment has been minimized.

Copy link

@straw1105 straw1105 commented Aug 1, 2018

How can various dependencies be obtained。 suach as:logger.info("Raw transaction: 0x{}", Hex.encodeHexString(tx.getEncoded()));
How to get Hex Depend on the package

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.