Large chain id support for private transactions (#1452)

Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Jason Frame 6 years ago committed by GitHub
parent 052e3f93fc
commit 86e77be924
  1. 3
      acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/eea/PrivateTransactionBuilder.java
  2. 61
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransaction.java
  3. 2
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionHandlerTest.java
  4. 74
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionTest.java
  5. 2
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/privacy/EeaGetTransactionReceiptTest.java

@ -21,6 +21,7 @@ import tech.pegasys.pantheon.ethereum.privacy.PrivateTransaction;
import tech.pegasys.pantheon.ethereum.rlp.RLP;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.math.BigInteger;
import java.util.List;
public class PrivateTransactionBuilder {
@ -124,7 +125,7 @@ public class PrivateTransactionBuilder {
.value(Wei.ZERO)
.payload(payload)
.sender(from)
.chainId(2018)
.chainId(BigInteger.valueOf(2018))
.privateFrom(privateFrom)
.privateFor(privateFor)
.restriction(BytesValue.wrap("restricted".getBytes(UTF_8)))

@ -33,20 +33,22 @@ import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalInt;
/** An operation submitted by an external actor to be applied to the system. */
public class PrivateTransaction {
// Used for transactions that are not tied to a specific chain
// (e.g. does not have a chain id associated with it).
private static final int REPLAY_UNPROTECTED_V_BASE = 27;
private static final BigInteger REPLAY_UNPROTECTED_V_BASE = BigInteger.valueOf(27);
private static final BigInteger REPLAY_UNPROTECTED_V_BASE_PLUS_1 = BigInteger.valueOf(28);
private static final int REPLAY_PROTECTED_V_BASE = 35;
private static final BigInteger REPLAY_PROTECTED_V_BASE = BigInteger.valueOf(35);
// The v signature parameter starts at 36 because 1 is the first valid chainId so:
// chainId > 1 implies that 2 * chainId + V_BASE > 36.
private static final int REPLAY_PROTECTED_V_MIN = 36;
private static final BigInteger REPLAY_PROTECTED_V_MIN = BigInteger.valueOf(36);
private static final BigInteger TWO = BigInteger.valueOf(2);
private final long nonce;
@ -62,7 +64,7 @@ public class PrivateTransaction {
private final BytesValue payload;
private final OptionalInt chainId;
private final Optional<BigInteger> chainId;
private final BytesValue privateFrom;
@ -98,14 +100,14 @@ public class PrivateTransaction {
.value(input.readUInt256Scalar(Wei::wrap))
.payload(input.readBytesValue());
final int v = input.readIntScalar();
final BigInteger v = input.readBigIntegerScalar();
final byte recId;
int chainId = -1;
if (v == REPLAY_UNPROTECTED_V_BASE || v == REPLAY_UNPROTECTED_V_BASE + 1) {
recId = (byte) (v - REPLAY_UNPROTECTED_V_BASE);
} else if (v > REPLAY_PROTECTED_V_MIN) {
chainId = (v - REPLAY_PROTECTED_V_BASE) / 2;
recId = (byte) (v - (2 * chainId + REPLAY_PROTECTED_V_BASE));
Optional<BigInteger> chainId = Optional.empty();
if (v.equals(REPLAY_UNPROTECTED_V_BASE) || v.equals(REPLAY_UNPROTECTED_V_BASE_PLUS_1)) {
recId = v.subtract(REPLAY_UNPROTECTED_V_BASE).byteValueExact();
} else if (v.compareTo(REPLAY_PROTECTED_V_MIN) > 0) {
chainId = Optional.of(v.subtract(REPLAY_PROTECTED_V_BASE).divide(TWO));
recId = v.subtract(TWO.multiply(chainId.get()).add(REPLAY_PROTECTED_V_BASE)).byteValueExact();
} else {
throw new RuntimeException(
String.format("An unsupported encoded `v` value of %s was found", v));
@ -119,8 +121,8 @@ public class PrivateTransaction {
input.leaveList();
chainId.ifPresent(builder::chainId);
return builder
.chainId(chainId)
.signature(signature)
.privateFrom(privateFrom)
.privateFor(privateFor)
@ -158,7 +160,7 @@ public class PrivateTransaction {
final SECP256K1.Signature signature,
final BytesValue payload,
final Address sender,
final int chainId,
final Optional<BigInteger> chainId,
final BytesValue privateFrom,
final List<BytesValue> privateFor,
final BytesValue restriction) {
@ -170,7 +172,7 @@ public class PrivateTransaction {
this.signature = signature;
this.payload = payload;
this.sender = sender;
this.chainId = chainId > 0 ? OptionalInt.of(chainId) : OptionalInt.empty();
this.chainId = chainId;
this.privateFrom = privateFrom;
this.privateFor = privateFor;
this.restriction = restriction;
@ -250,7 +252,7 @@ public class PrivateTransaction {
*
* @return the transaction chain id if it exists; otherwise {@code OptionalInt.empty()}
*/
public OptionalInt getChainId() {
public Optional<BigInteger> getChainId() {
return chainId;
}
@ -340,7 +342,7 @@ public class PrivateTransaction {
}
private void writeSignature(final RLPOutput out) {
out.writeIntScalar(getV());
out.writeBigIntegerScalar(getV());
out.writeBigIntegerScalar(getSignature().getR());
out.writeBigIntegerScalar(getSignature().getS());
}
@ -353,12 +355,13 @@ public class PrivateTransaction {
return signature.getS();
}
public int getV() {
final int v;
public BigInteger getV() {
final BigInteger v;
final BigInteger recId = BigInteger.valueOf(signature.getRecId());
if (!chainId.isPresent()) {
v = signature.getRecId() + REPLAY_UNPROTECTED_V_BASE;
v = recId.add(REPLAY_UNPROTECTED_V_BASE);
} else {
v = (getSignature().getRecId() + REPLAY_PROTECTED_V_BASE + 2 * chainId.getAsInt());
v = recId.add(REPLAY_PROTECTED_V_BASE).add(TWO.multiply(chainId.get()));
}
return v;
}
@ -414,7 +417,7 @@ public class PrivateTransaction {
final Address to,
final Wei value,
final BytesValue payload,
final OptionalInt chainId,
final Optional<BigInteger> chainId,
final BytesValue privateFrom,
final List<BytesValue> privateFor,
final BytesValue restriction) {
@ -429,7 +432,7 @@ public class PrivateTransaction {
out.writeUInt256Scalar(value);
out.writeBytesValue(payload);
if (chainId.isPresent()) {
out.writeIntScalar(chainId.getAsInt());
out.writeBigIntegerScalar(chainId.get());
out.writeUInt256Scalar(UInt256.ZERO);
out.writeUInt256Scalar(UInt256.ZERO);
}
@ -485,7 +488,7 @@ public class PrivateTransaction {
if (getTo().isPresent()) sb.append("to=").append(getTo().get()).append(", ");
sb.append("value=").append(getValue()).append(", ");
sb.append("sig=").append(getSignature()).append(", ");
if (chainId.isPresent()) sb.append("chainId=").append(getChainId().getAsInt()).append(", ");
if (chainId.isPresent()) sb.append("chainId=").append(getChainId().get()).append(", ");
sb.append("payload=").append(getPayload());
sb.append("privateFrom=").append(getPrivateFrom());
sb.append("privateFor=").append(Arrays.toString(getPrivateFor().toArray()));
@ -518,7 +521,7 @@ public class PrivateTransaction {
protected Address sender;
protected int chainId = -1;
protected Optional<BigInteger> chainId = Optional.empty();
protected BytesValue privateFrom;
@ -526,8 +529,8 @@ public class PrivateTransaction {
protected BytesValue restriction;
public Builder chainId(final int chainId) {
this.chainId = chainId;
public Builder chainId(final BigInteger chainId) {
this.chainId = Optional.of(chainId);
return this;
}
@ -611,8 +614,6 @@ public class PrivateTransaction {
}
protected SECP256K1.Signature computeSignature(final SECP256K1.KeyPair keys) {
final OptionalInt optionalChainId =
chainId > 0 ? OptionalInt.of(chainId) : OptionalInt.empty();
final Bytes32 hash =
computeSenderRecoveryHash(
nonce,
@ -621,7 +622,7 @@ public class PrivateTransaction {
to,
value,
payload,
optionalChainId,
chainId,
privateFrom,
privateFor,
restriction);

@ -60,7 +60,7 @@ public class PrivateTransactionHandlerTest {
.value(Wei.ZERO)
.payload(BytesValue.fromHexString("0x"))
.sender(Address.fromHexString("0xfe3b557e8fb62b89f4916b721be55ceb828dbd73"))
.chainId(2018)
.chainId(BigInteger.valueOf(2018))
.privateFrom(
BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)))
.privateFor(

@ -67,6 +67,23 @@ public class PrivateTransactionTest {
+ "44f6e766966746a69697a706a52742b4854754642733d8a7265737472696"
+ "3746564";
private static final String VALID_SIGNED_PRIVATE_TRANSACTION_LARGE_CHAINID_RLP =
"0xf901a9808203e8832dc6c08080b8ef608060405234801561001057600080"
+ "fd5b5060d08061001f6000396000f3fe60806040526004361060485763ff"
+ "ffffff7c0100000000000000000000000000000000000000000000000000"
+ "00000060003504166360fe47b18114604d5780636d4ce63c146075575b60"
+ "0080fd5b348015605857600080fd5b50607360048036036020811015606d"
+ "57600080fd5b50356099565b005b348015608057600080fd5b506087609e"
+ "565b60408051918252519081900360200190f35b600055565b6000549056"
+ "fea165627a7a72305820cb1d0935d14b589300b12fcd0ab849a7e9019c81"
+ "da24d6daa4f6b2f003d1b0180029850100000022a0ebccb6952d7ad4eb5c"
+ "1d4da2f67a833f66c1b9127e0c592224dd24210104a095a07d35a1bbc54f"
+ "fa5b2dc9f315b545238575c8960108076036c6ffcafedddf4d22ac413161"
+ "56744d784c4355486d425648586f5a7a7a42675062572f776a3561784470"
+ "573958386c393153476f3dedac4b6f32625671442b6e4e6c4e594c354545"
+ "37793349644f6e766966746a69697a706a52742b4854754642733d8a7265"
+ "7374726963746564";
private static final PrivateTransaction VALID_PRIVATE_TRANSACTION =
new PrivateTransaction(
0L,
@ -85,7 +102,7 @@ public class PrivateTransactionTest {
Byte.valueOf("0")),
BytesValue.fromHexString("0x"),
Address.wrap(BytesValue.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")),
0,
Optional.empty(),
BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)),
Lists.newArrayList(
BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)),
@ -113,7 +130,42 @@ public class PrivateTransactionTest {
+ "0029"))
.sender(
Address.wrap(BytesValue.fromHexString("0x1c9a6e1ee3b7ac6028e786d9519ae3d24ee31e79")))
.chainId(4)
.chainId(BigInteger.valueOf(4))
.privateFrom(
BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)))
.privateFor(
Lists.newArrayList(
BytesValue.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8))))
.restriction(BytesValue.wrap("restricted".getBytes(UTF_8)))
.signAndBuild(
SECP256K1.KeyPair.create(
SECP256K1.PrivateKey.create(
new BigInteger(
"853d7f0010fd86d0d7811c1f9d968ea89a24484a8127b4a483ddf5d2cfec766d",
16))));
private static final PrivateTransaction VALID_SIGNED_PRIVATE_TRANSACTION_LARGE_CHAINID =
PrivateTransaction.builder()
.nonce(0)
.gasPrice(Wei.of(1000))
.gasLimit(3000000)
.to(null)
.value(Wei.ZERO)
.payload(
BytesValue.fromHexString(
"0x608060405234801561001057600080fd5b5060d08061001f6000396000"
+ "f3fe60806040526004361060485763ffffffff7c010000000000"
+ "0000000000000000000000000000000000000000000000600035"
+ "04166360fe47b18114604d5780636d4ce63c146075575b600080"
+ "fd5b348015605857600080fd5b50607360048036036020811015"
+ "606d57600080fd5b50356099565b005b348015608057600080fd"
+ "5b506087609e565b60408051918252519081900360200190f35b"
+ "600055565b6000549056fea165627a7a72305820cb1d0935d14b"
+ "589300b12fcd0ab849a7e9019c81da24d6daa4f6b2f003d1b018"
+ "0029"))
.sender(
Address.wrap(BytesValue.fromHexString("0x1c9a6e1ee3b7ac6028e786d9519ae3d24ee31e79")))
.chainId(BigInteger.valueOf(2147483647))
.privateFrom(
BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)))
.privateFor(
@ -165,4 +217,22 @@ public class PrivateTransactionTest {
PrivateTransaction.readFrom(
new BytesValueRLPInput(BytesValue.fromHexString(INVALID_RLP), false));
}
@Test
public void testWriteToWithLargeChainId() {
BytesValueRLPOutput bvrlpo = new BytesValueRLPOutput();
VALID_SIGNED_PRIVATE_TRANSACTION_LARGE_CHAINID.writeTo(bvrlpo);
assertEquals(VALID_SIGNED_PRIVATE_TRANSACTION_LARGE_CHAINID_RLP, bvrlpo.encoded().toString());
}
@Test
public void testReadFromWithLargeChainId() {
PrivateTransaction p =
PrivateTransaction.readFrom(
new BytesValueRLPInput(
BytesValue.fromHexString(VALID_SIGNED_PRIVATE_TRANSACTION_LARGE_CHAINID_RLP),
false));
assertEquals(VALID_SIGNED_PRIVATE_TRANSACTION_LARGE_CHAINID, p);
}
}

@ -91,7 +91,7 @@ public class EeaGetTransactionReceiptTest {
+ "5820cb1d0935d14b589300b12fcd0ab849a7e9019c81da24d6"
+ "daa4f6b2f003d1b0180029"))
.sender(sender)
.chainId(2018)
.chainId(BigInteger.valueOf(2018))
.privateFrom(
BytesValue.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)))
.privateFor(

Loading…
Cancel
Save