ContractInvocationInfoExtractor.java

  1. /*
  2.  * @copyright defined in LICENSE.txt
  3.  */

  4. package hera.spec.transaction;

  5. import static hera.util.ValidationUtils.assertEquals;
  6. import static hera.util.ValidationUtils.assertTrue;
  7. import static org.slf4j.LoggerFactory.getLogger;

  8. import com.fasterxml.jackson.databind.JsonNode;
  9. import com.fasterxml.jackson.databind.ObjectMapper;
  10. import com.fasterxml.jackson.databind.ObjectReader;
  11. import com.fasterxml.jackson.databind.node.ArrayNode;
  12. import com.fasterxml.jackson.databind.node.BooleanNode;
  13. import com.fasterxml.jackson.databind.node.NullNode;
  14. import com.fasterxml.jackson.databind.node.NumericNode;
  15. import com.fasterxml.jackson.databind.node.ObjectNode;
  16. import com.fasterxml.jackson.databind.node.TextNode;
  17. import com.fasterxml.jackson.databind.node.ValueNode;
  18. import hera.api.model.BigNumber;
  19. import hera.api.model.ContractAddress;
  20. import hera.api.model.ContractFunction;
  21. import hera.api.model.ContractInvocation;
  22. import hera.api.model.Transaction;
  23. import hera.exception.HerajException;
  24. import hera.spec.AergoSpec;
  25. import java.util.ArrayList;
  26. import java.util.HashMap;
  27. import java.util.Iterator;
  28. import java.util.List;
  29. import java.util.Map;
  30. import java.util.Map.Entry;
  31. import lombok.AccessLevel;
  32. import lombok.NoArgsConstructor;
  33. import org.slf4j.Logger;

  34. @NoArgsConstructor(access = AccessLevel.PACKAGE)
  35. public class ContractInvocationInfoExtractor
  36.     implements TransactionInfoExtractor<ContractInvocation> {

  37.   protected static final ObjectReader reader = new ObjectMapper().reader();

  38.   protected final Logger logger = getLogger(getClass());

  39.   @Override
  40.   public ContractInvocation extract(final Transaction transaction) {
  41.     try {
  42.       final JsonNode jsonNode = reader.readTree(transaction.getPayload().getInputStream());
  43.       final JsonNode nameNode = jsonNode.findValue("Name");
  44.       final JsonNode argsNode = jsonNode.findValue("Args");

  45.       final ContractFunction function = parseToContractFunction(nameNode);
  46.       final List<Object> args = parseToList(argsNode);
  47.       final ContractInvocation recovered = ContractInvocation.newBuilder()
  48.           .address(transaction.getRecipient().adapt(ContractAddress.class))
  49.           .function(function)
  50.           .amount(transaction.getAmount())
  51.           .args(args)
  52.           .build();
  53.       return recovered;
  54.     } catch (Exception e) {
  55.       throw new HerajException(e);
  56.     }
  57.   }

  58.   protected ContractFunction parseToContractFunction(final JsonNode jsonNode) {
  59.     final String functionName = jsonNode.asText();
  60.     return new ContractFunction(functionName);
  61.   }

  62.   protected List<Object> parseToList(final JsonNode jsonNode) {
  63.     assertTrue(jsonNode instanceof ArrayNode);

  64.     final List<Object> ret = new ArrayList<>();
  65.     final int size = jsonNode.size();
  66.     for (int i = 0; i < size; ++i) {
  67.       final JsonNode next = jsonNode.get(i);
  68.       Object parsedValue = null;
  69.       if (next instanceof ArrayNode) {
  70.         parsedValue = parseToList((ArrayNode) next);
  71.       } else if (next instanceof ObjectNode) {
  72.         if (null != next.get(AergoSpec.BIGNUM_JSON_KEY)) {
  73.           parsedValue = parseToBigNumber((ObjectNode) next);
  74.         } else {
  75.           parsedValue = parseToMap((ObjectNode) next);
  76.         }
  77.       } else {
  78.         parsedValue = parseValue((ValueNode) next);
  79.       }
  80.       ret.add(parsedValue);
  81.     }
  82.     return ret;
  83.   }

  84.   protected Map<String, Object> parseToMap(final ObjectNode jsonNode) {
  85.     assertTrue(jsonNode instanceof ObjectNode);

  86.     final Map<String, Object> ret = new HashMap<>();
  87.     final Iterator<Entry<String, JsonNode>> it = jsonNode.fields();
  88.     while (it.hasNext()) {
  89.       final Entry<String, JsonNode> nextObjectNode = it.next();
  90.       final String key = nextObjectNode.getKey();
  91.       final JsonNode value = nextObjectNode.getValue();
  92.       Object parsedValue = null;
  93.       if (value instanceof ArrayNode) {
  94.         parsedValue = parseToList((ArrayNode) value);
  95.       } else if (value instanceof ObjectNode) {
  96.         if (null != value.get(AergoSpec.BIGNUM_JSON_KEY)) {
  97.           parsedValue = parseToBigNumber((ObjectNode) value);
  98.         } else {
  99.           parsedValue = parseToMap((ObjectNode) value);
  100.         }
  101.       } else {
  102.         parsedValue = parseValue((ValueNode) value);
  103.       }
  104.       ret.put(key, parsedValue);
  105.     }
  106.     return ret;
  107.   }

  108.   protected BigNumber parseToBigNumber(final ObjectNode jsonNode) {
  109.     assertTrue(jsonNode instanceof ObjectNode);
  110.     assertEquals(1, jsonNode.size());

  111.     final JsonNode bigNumberValueNode = jsonNode.get(AergoSpec.BIGNUM_JSON_KEY);
  112.     if (null == bigNumberValueNode) {
  113.       throw new IllegalArgumentException("JsonNode is not bignum value: " + jsonNode.toString());
  114.     }

  115.     if (!(bigNumberValueNode instanceof TextNode)) {
  116.       throw new IllegalArgumentException(
  117.           "JsonNode value is not bignum value: " + bigNumberValueNode.toString());
  118.     }

  119.     return new BigNumber(bigNumberValueNode.asText());
  120.   }

  121.   protected Object parseValue(final ValueNode valueNode) {
  122.     if (valueNode instanceof BooleanNode) {
  123.       return parseToBoolean(valueNode);
  124.     } else if (valueNode instanceof NullNode) {
  125.       return parseToNull(valueNode);
  126.     } else if (valueNode instanceof NumericNode) {
  127.       return parseToNumber(valueNode);
  128.     } else if (valueNode instanceof TextNode) {
  129.       return parseToString(valueNode);
  130.     } else {
  131.       throw new UnsupportedOperationException(
  132.           "Unsupported node type: " + valueNode.getClass().getName());
  133.     }
  134.   }

  135.   protected String parseToString(final JsonNode jsonNode) {
  136.     return jsonNode.asText();
  137.   }

  138.   protected Number parseToNumber(final JsonNode jsonNode) {
  139.     return ((NumericNode) jsonNode).numberValue();
  140.   }

  141.   protected Boolean parseToBoolean(final JsonNode jsonNode) {
  142.     return jsonNode.asBoolean();
  143.   }

  144.   protected String parseToNull(final JsonNode jsonNode) {
  145.     return null;
  146.   }

  147. }