Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

Commit

Permalink
Large chain id support for private transactions (#1452)
Browse files Browse the repository at this point in the history
  • Loading branch information
jframe authored May 17, 2019
1 parent 102c34c commit b33a655
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
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 {
Expand Down Expand Up @@ -124,7 +125,7 @@ public String build(final TransactionType type) {
.value(Wei.ZERO)
.payload(payload)
.sender(from)
.chainId(2018)
.chainId(BigInteger.valueOf(2018))
.privateFrom(privateFrom)
.privateFor(privateFor)
.restriction(BytesValue.wrap("restricted".getBytes(UTF_8)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,22 @@
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;

Expand All @@ -62,7 +64,7 @@ public class PrivateTransaction {

private final BytesValue payload;

private final OptionalInt chainId;
private final Optional<BigInteger> chainId;

private final BytesValue privateFrom;

Expand Down Expand Up @@ -98,14 +100,14 @@ public static PrivateTransaction readFrom(final RLPInput input) throws RLPExcept
.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));
Expand All @@ -119,8 +121,8 @@ public static PrivateTransaction readFrom(final RLPInput input) throws RLPExcept

input.leaveList();

chainId.ifPresent(builder::chainId);
return builder
.chainId(chainId)
.signature(signature)
.privateFrom(privateFrom)
.privateFor(privateFor)
Expand Down Expand Up @@ -158,7 +160,7 @@ protected 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) {
Expand All @@ -170,7 +172,7 @@ protected 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;
Expand Down Expand Up @@ -250,7 +252,7 @@ public BytesValue getPayload() {
*
* @return the transaction chain id if it exists; otherwise {@code OptionalInt.empty()}
*/
public OptionalInt getChainId() {
public Optional<BigInteger> getChainId() {
return chainId;
}

Expand Down Expand Up @@ -340,7 +342,7 @@ public void writeTo(final RLPOutput out) {
}

private void writeSignature(final RLPOutput out) {
out.writeIntScalar(getV());
out.writeBigIntegerScalar(getV());
out.writeBigIntegerScalar(getSignature().getR());
out.writeBigIntegerScalar(getSignature().getS());
}
Expand All @@ -353,12 +355,13 @@ public BigInteger getS() {
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;
}
Expand Down Expand Up @@ -414,7 +417,7 @@ private static Bytes32 computeSenderRecoveryHash(
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) {
Expand All @@ -429,7 +432,7 @@ private static Bytes32 computeSenderRecoveryHash(
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);
}
Expand Down Expand Up @@ -485,7 +488,7 @@ public String toString() {
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()));
Expand Down Expand Up @@ -518,16 +521,16 @@ public static class Builder {

protected Address sender;

protected int chainId = -1;
protected Optional<BigInteger> chainId = Optional.empty();

protected BytesValue privateFrom;

protected List<BytesValue> privateFor;

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;
}

Expand Down Expand Up @@ -611,8 +614,6 @@ public PrivateTransaction signAndBuild(final SECP256K1.KeyPair keys) {
}

protected SECP256K1.Signature computeSignature(final SECP256K1.KeyPair keys) {
final OptionalInt optionalChainId =
chainId > 0 ? OptionalInt.of(chainId) : OptionalInt.empty();
final Bytes32 hash =
computeSenderRecoveryHash(
nonce,
Expand All @@ -621,7 +622,7 @@ protected SECP256K1.Signature computeSignature(final SECP256K1.KeyPair keys) {
to,
value,
payload,
optionalChainId,
chainId,
privateFrom,
privateFor,
restriction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)),
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -165,4 +217,22 @@ public void testReadFromInvalid() {
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit b33a655

Please sign in to comment.