diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/transaction/TransactionSimulator.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/transaction/TransactionSimulator.java index a94373ea98..fa08fd0ae4 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/transaction/TransactionSimulator.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/transaction/TransactionSimulator.java @@ -72,6 +72,10 @@ public class TransactionSimulator { return process(callParams, header); } + public Optional processAtHead(final CallParameter callParams) { + return process(callParams, blockchain.getChainHeadHeader()); + } + private Optional process( final CallParameter callParams, final BlockHeader header) { if (header == null) { diff --git a/ethereum/permissioning/build.gradle b/ethereum/permissioning/build.gradle index b73c2fb9ac..97e402265c 100644 --- a/ethereum/permissioning/build.gradle +++ b/ethereum/permissioning/build.gradle @@ -28,12 +28,16 @@ jar { dependencies { implementation project(':util') implementation project(':ethereum:core') + implementation project(':crypto') implementation 'com.google.guava:guava' implementation 'net.consensys.cava:cava-toml' implementation 'org.apache.logging.log4j:log4j-api' + testImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts') + testImplementation project(':config') testImplementation 'junit:junit' + testImplementation 'io.vertx:vertx-core' testImplementation 'org.assertj:assertj-core' testImplementation 'org.mockito:mockito-core' } diff --git a/ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/SmartContractPermissioningController.java b/ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/SmartContractPermissioningController.java index 70ed3b378a..a4b8db0b82 100644 --- a/ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/SmartContractPermissioningController.java +++ b/ethereum/permissioning/src/main/java/tech/pegasys/pantheon/ethereum/permissioning/SmartContractPermissioningController.java @@ -12,24 +12,158 @@ */ package tech.pegasys.pantheon.ethereum.permissioning; +import static java.nio.charset.StandardCharsets.UTF_8; + +import tech.pegasys.pantheon.crypto.Hash; import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningProvider; +import tech.pegasys.pantheon.ethereum.transaction.CallParameter; import tech.pegasys.pantheon.ethereum.transaction.TransactionSimulator; +import tech.pegasys.pantheon.ethereum.transaction.TransactionSimulatorResult; +import tech.pegasys.pantheon.util.bytes.BytesValue; +import tech.pegasys.pantheon.util.bytes.BytesValues; import tech.pegasys.pantheon.util.enode.EnodeURL; -public class SmartContractPermissioningController implements NodePermissioningProvider { +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.Optional; +/** + * Controller that can read from a smart contract that exposes the permissioning calls + * connectionAllowedIpv4(bytes32,bytes32,bytes4,uint16,bytes32,bytes32,bytes4,uint16) + * connectionAllowedIpv6(bytes32,bytes32,bytes16,uint16,bytes32,bytes32,bytes16,uint16) + */ +public class SmartContractPermissioningController implements NodePermissioningProvider { private final Address contractAddress; private final TransactionSimulator transactionSimulator; + // full function signature for ipv4 call + private static final String IPV4_FUNCTION_SIGNATURE = + "connectionAllowedIpv4(bytes32,bytes32,bytes4,uint16,bytes32,bytes32,bytes4,uint16)"; + // hashed function signature for ipv4 call + private static final BytesValue IPV4_FUNCTION_SIGNATURE_HASH = + hashSignature(IPV4_FUNCTION_SIGNATURE); + // full function signature for ipv6 call + private static final String IPV6_FUNCTION_SIGNATURE = + "connectionAllowedIpv6(bytes32,bytes32,bytes16,uint16,bytes32,bytes32,bytes16,uint16)"; + // hashed function signature for ipv6 call + private static final BytesValue IPV6_FUNCTION_SIGNATURE_HASH = + hashSignature(IPV6_FUNCTION_SIGNATURE); + + // The first 4 bytes of the hash of the full textual signature of the function is used in + // contract calls to determine the function being called + private static BytesValue hashSignature(final String signature) { + return Hash.keccak256(BytesValue.of(signature.getBytes(UTF_8))).slice(0, 4); + } + + // True from a contract is 1 filled to 32 bytes + private static final BytesValue TRUE_RESPONSE; + + static { + final byte[] trueValue = new byte[32]; + trueValue[31] = (byte) (0xFF & 1L); + TRUE_RESPONSE = BytesValue.wrap(trueValue); + } + + /** + * Creates a permissioning controller attached to a blockchain + * + * @param contractAddress The address at which the permissioning smart contract resides + * @param transactionSimulator A transaction simulator with attached blockchain and world state + */ public SmartContractPermissioningController( final Address contractAddress, final TransactionSimulator transactionSimulator) { this.contractAddress = contractAddress; this.transactionSimulator = transactionSimulator; } + /** + * Check whether a given connection from the source to destination enode should be permitted + * + * @param sourceEnode The enode url of the node initiating the connection + * @param destinationEnode The enode url of the node receiving the connection + * @return boolean of whether or not to permit the connection to occur + */ @Override public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode) { - return true; + final BytesValue payload = createPayload(sourceEnode, destinationEnode); + final CallParameter callParams = + new CallParameter(null, contractAddress, -1, null, null, payload); + + final Optional result = + transactionSimulator.processAtHead(callParams); + + return result.map(r -> checkTransactionResult(r.getOutput())).orElse(false); + } + + // Checks the returned bytes from the permissioning contract call to see if it's a value we + // understand + private Boolean checkTransactionResult(final BytesValue result) { + // booleans are padded to 32 bytes + if (result.size() != 32) { + throw new IllegalArgumentException("Unexpected result size"); + } + + // 0 is false + if (result.isZero()) { + return false; + // 1 filled to 32 bytes is true + } else if (result.compareTo(TRUE_RESPONSE) == 0) { + return true; + // Anything else is wrong + } else { + throw new IllegalStateException("Unexpected result form"); + } + } + + // Assemble the bytevalue payload to call the contract + private BytesValue createPayload(final EnodeURL sourceEnode, final EnodeURL destinationEnode) { + final BytesValue signature; + // Grab the right function signature based on the enodes provided + if (sourceEnode.getInetAddress() instanceof Inet4Address + && destinationEnode.getInetAddress() instanceof Inet4Address) { + signature = IPV4_FUNCTION_SIGNATURE_HASH; + } else if (sourceEnode.getInetAddress() instanceof Inet6Address + && destinationEnode.getInetAddress() instanceof Inet6Address) { + signature = IPV6_FUNCTION_SIGNATURE_HASH; + } else { + // If we got mixed mode enodes then it's wrong + throw new IllegalArgumentException( + "No payload possible for checking an ipv4 to ipv6 connection"); + } + return BytesValues.concatenate( + signature, encodeEnodeUrl(sourceEnode), encodeEnodeUrl(destinationEnode)); + } + + private BytesValue encodeEnodeUrl(final EnodeURL enode) { + return BytesValues.concatenate( + encodeEnodeId(enode.getNodeId()), + encodeIp(enode.getInetAddress()), + encodePort(enode.getListeningPort())); + } + + // As a function parameter an ip needs to be the appropriate number of bytes, big endian, and + // filled to 32 bytes + private BytesValue encodeIp(final InetAddress addr) { + // InetAddress deals with giving us the right number of bytes + final byte[] address = addr.getAddress(); + final byte[] res = new byte[32]; + System.arraycopy(address, 0, res, 0, address.length); + return BytesValue.wrap(res); + } + + // The port, a uint16, needs to be 2 bytes, little endian, and filled to 32 bytes + private BytesValue encodePort(final Integer port) { + final byte[] res = new byte[32]; + res[31] = (byte) ((port) & 0xFF); + res[30] = (byte) ((port >> 8) & 0xFF); + return BytesValue.wrap(res); + } + + // The enode high and low need to be 32 bytes each. They then get concatenated as they are + // adjacent parameters + private BytesValue encodeEnodeId(final String id) { + return BytesValue.fromHexString(id); } } diff --git a/ethereum/permissioning/src/test/java/tech/pegasys/pantheon/ethereum/permissioning/SmartContractPermissioningControllerTest.java b/ethereum/permissioning/src/test/java/tech/pegasys/pantheon/ethereum/permissioning/SmartContractPermissioningControllerTest.java new file mode 100644 index 0000000000..4981ef5a6a --- /dev/null +++ b/ethereum/permissioning/src/test/java/tech/pegasys/pantheon/ethereum/permissioning/SmartContractPermissioningControllerTest.java @@ -0,0 +1,152 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.permissioning; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; +import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain; +import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive; + +import tech.pegasys.pantheon.config.GenesisConfigFile; +import tech.pegasys.pantheon.ethereum.chain.GenesisState; +import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain; +import tech.pegasys.pantheon.ethereum.core.Address; +import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule; +import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; +import tech.pegasys.pantheon.ethereum.transaction.TransactionSimulator; +import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; +import tech.pegasys.pantheon.util.enode.EnodeURL; + +import java.io.IOException; + +import com.google.common.io.Resources; +import org.junit.Test; + +public class SmartContractPermissioningControllerTest { + + private SmartContractPermissioningController setupController( + final String resourceName, final String contractAddressString) throws IOException { + final ProtocolSchedule protocolSchedule = MainnetProtocolSchedule.create(); + + final String emptyContractFile = Resources.toString(Resources.getResource(resourceName), UTF_8); + final GenesisState genesisState = + GenesisState.fromConfig(GenesisConfigFile.fromConfig(emptyContractFile), protocolSchedule); + + final MutableBlockchain blockchain = createInMemoryBlockchain(genesisState.getBlock()); + final WorldStateArchive worldArchive = createInMemoryWorldStateArchive(); + + genesisState.writeStateTo(worldArchive.getMutable()); + + final TransactionSimulator ts = + new TransactionSimulator(blockchain, worldArchive, protocolSchedule); + final Address contractAddress = Address.fromHexString(contractAddressString); + + return new SmartContractPermissioningController(contractAddress, ts); + } + + @Test + public void testIpv4Included() throws IOException { + final SmartContractPermissioningController controller = + setupController( + "SmartContractPermissioningControllerTest/preseededSmartPermissioning.json", + "0x0000000000000000000000000000000000001234"); + + assertThat( + controller.isPermitted( + new EnodeURL( + "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:30303"), + new EnodeURL( + "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:30304"))) + .isTrue(); + } + + @Test + public void testIpv4DestinationMissing() throws IOException { + final SmartContractPermissioningController controller = + setupController( + "SmartContractPermissioningControllerTest/preseededSmartPermissioning.json", + "0x0000000000000000000000000000000000001234"); + + assertThat( + controller.isPermitted( + new EnodeURL( + "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:30303"), + new EnodeURL( + "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:30305"))) + .isFalse(); + } + + @Test + public void testIpv4SourceMissing() throws IOException { + final SmartContractPermissioningController controller = + setupController( + "SmartContractPermissioningControllerTest/preseededSmartPermissioning.json", + "0x0000000000000000000000000000000000001234"); + + assertThat( + controller.isPermitted( + new EnodeURL( + "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:30302"), + new EnodeURL( + "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:30304"))) + .isFalse(); + } + + @Test + public void testIpv6Included() throws IOException { + final SmartContractPermissioningController controller = + setupController( + "SmartContractPermissioningControllerTest/preseededSmartPermissioning.json", + "0x0000000000000000000000000000000000001234"); + + assertThat( + controller.isPermitted( + new EnodeURL( + "enode://1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab61@[1:2:3:4:5:6:7:8]:30303"), + new EnodeURL( + "enode://1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab62@[1:2:3:4:5:6:7:8]:30304"))) + .isTrue(); + } + + @Test + public void testIpv6SourceMissing() throws IOException { + final SmartContractPermissioningController controller = + setupController( + "SmartContractPermissioningControllerTest/preseededSmartPermissioning.json", + "0x0000000000000000000000000000000000001234"); + + assertThat( + controller.isPermitted( + new EnodeURL( + "enode://1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab63@[1:2:3:4:5:6:7:8]:30303"), + new EnodeURL( + "enode://1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab62@[1:2:3:4:5:6:7:8]:30304"))) + .isFalse(); + } + + @Test + public void testIpv6DestinationMissing() throws IOException { + final SmartContractPermissioningController controller = + setupController( + "SmartContractPermissioningControllerTest/preseededSmartPermissioning.json", + "0x0000000000000000000000000000000000001234"); + + assertThat( + controller.isPermitted( + new EnodeURL( + "enode://1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab61@[1:2:3:4:5:6:7:8]:30303"), + new EnodeURL( + "enode://1234000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab63@[1:2:3:4:5:6:7:8]:30304"))) + .isFalse(); + } +} diff --git a/ethereum/permissioning/src/test/resources/SmartContractPermissioningControllerTest/preseededSmartPermissioning.json b/ethereum/permissioning/src/test/resources/SmartContractPermissioningControllerTest/preseededSmartPermissioning.json new file mode 100644 index 0000000000..d6da94ff09 --- /dev/null +++ b/ethereum/permissioning/src/test/resources/SmartContractPermissioningControllerTest/preseededSmartPermissioning.json @@ -0,0 +1,62 @@ +{ + "config": { + "chainId": 2018, + "homesteadBlock": 0, + "daoForkBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "constantinopleFixBlock": 0, + "ethash": { + "fixeddifficulty": 100 + } + }, + "nonce": "0x42", + "timestamp": "0x0", + "extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa", + "gasLimit": "0x1000000", + "difficulty": "0x10000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "0x0000000000000000000000000000000000001234": { + "comment": "Empty smart permissioning contract", + "balance": "0", + "code": "60806040526004361061009e576000357c01000000000000000000000000000000000000000000000000000000009004806327d11f21146100a3578063680fc99c1461016b5780636863d6c0146101e75780637af5f9ac1461027b57806384bc07a7146102eb57806393f3d9b1146103735780639b9d2bce1461045c578063b0517d701461053c578063c909760d146105ac578063dcc25bc914610628575b600080fd5b3480156100af57600080fd5b5061015160048036036101008110156100c757600080fd5b81019080803590602001909291908035906020019092919080356fffffffffffffffffffffffffffffffff19169060200190929190803561ffff169060200190929190803590602001909291908035906020019092919080356fffffffffffffffffffffffffffffffff19169060200190929190803561ffff16906020019092919050505061071d565b604051808215151515815260200191505060405180910390f35b34801561017757600080fd5b506101e56004803603608081101561018e57600080fd5b81019080803590602001909291908035906020019092919080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190803561ffff16906020019092919050505061074d565b005b3480156101f357600080fd5b506102616004803603608081101561020a57600080fd5b81019080803590602001909291908035906020019092919080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190803561ffff16906020019092919050505061089b565b604051808215151515815260200191505060405180910390f35b34801561028757600080fd5b506102e96004803603608081101561029e57600080fd5b81019080803590602001909291908035906020019092919080356fffffffffffffffffffffffffffffffff19169060200190929190803561ffff1690602001909291905050506109a6565b005b3480156102f757600080fd5b506103596004803603608081101561030e57600080fd5b81019080803590602001909291908035906020019092919080356fffffffffffffffffffffffffffffffff19169060200190929190803561ffff169060200190929190505050610b05565b604051808215151515815260200191505060405180910390f35b34801561037f57600080fd5b506103e16004803603608081101561039657600080fd5b81019080803590602001909291908035906020019092919080356fffffffffffffffffffffffffffffffff19169060200190929190803561ffff169060200190929190505050610beb565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610421578082015181840152602081019050610406565b50505050905090810190601f16801561044e5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561046857600080fd5b50610522600480360361010081101561048057600080fd5b81019080803590602001909291908035906020019092919080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190803561ffff169060200190929190803590602001909291908035906020019092919080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190803561ffff169060200190929190505050610c5d565b604051808215151515815260200191505060405180910390f35b34801561054857600080fd5b506105aa6004803603608081101561055f57600080fd5b81019080803590602001909291908035906020019092919080356fffffffffffffffffffffffffffffffff19169060200190929190803561ffff169060200190929190505050610c8d565b005b3480156105b857600080fd5b50610626600480360360808110156105cf57600080fd5b81019080803590602001909291908035906020019092919080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190803561ffff169060200190929190505050610dcf565b005b34801561063457600080fd5b506106a26004803603608081101561064b57600080fd5b81019080803590602001909291908035906020019092919080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190803561ffff169060200190929190505050610f46565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156106e25780820151818401526020810190506106c7565b50505050905090810190601f16801561070f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b600061072b89898989610b05565b801561073f575061073e85858585610b05565b5b905098975050505050505050565b610755610fd0565b608060405190810160405280868152602001858152602001847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020018361ffff16815250905060606107aa86868686610f46565b9050816001826040518082805190602001908083835b6020831015156107e557805182526020820191506020810190506020830392506107c0565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020600082015181600001556020820151816001015560408201518160020160006101000a81548163ffffffff02191690837c01000000000000000000000000000000000000000000000000000000009004021790555060608201518160020160046101000a81548161ffff021916908361ffff160217905550905050505050505050565b600060606108ab86868686610f46565b905060006001826040518082805190602001908083835b6020831015156108e757805182526020820191506020810190506020830392506108c2565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020905060007c0100000000000000000000000000000000000000000000000000000000028160020160009054906101000a90047c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916111561099b5760019250505061099e565b50505b949350505050565b60606109b485858585610beb565b90506109be611022565b60806040519081016040528060006001028152602001600060010281526020016000700100000000000000000000000000000000026fffffffffffffffffffffffffffffffff19168152602001600061ffff168152509050806000836040518082805190602001908083835b602083101515610a4f5780518252602082019150602081019050602083039250610a2a565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020600082015181600001556020820151816001015560408201518160020160006101000a8154816fffffffffffffffffffffffffffffffff02191690837001000000000000000000000000000000009004021790555060608201518160020160106101000a81548161ffff021916908361ffff160217905550905050505050505050565b60006060610b1586868686610beb565b9050600080826040518082805190602001908083835b602083101515610b505780518252602082019150602081019050602083039250610b2b565b6001836020036101000a038019825116818451168082178552505050505050905001915050908152602001604051809103902090506000700100000000000000000000000000000000028160020160009054906101000a9004700100000000000000000000000000000000026fffffffffffffffffffffffffffffffff19161115610be057600192505050610be3565b50505b949350505050565b60608484848460405160200180858152602001848152602001836fffffffffffffffffffffffffffffffff19166fffffffffffffffffffffffffffffffff191681526020018261ffff1661ffff1681526020019450505050506040516020818303038152906040529050949350505050565b6000610c6b8989898961089b565b8015610c7f5750610c7e8585858561089b565b5b905098975050505050505050565b610c95611022565b608060405190810160405280868152602001858152602001846fffffffffffffffffffffffffffffffff191681526020018361ffff1681525090506060610cde86868686610beb565b9050816000826040518082805190602001908083835b602083101515610d195780518252602082019150602081019050602083039250610cf4565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020600082015181600001556020820151816001015560408201518160020160006101000a8154816fffffffffffffffffffffffffffffffff02191690837001000000000000000000000000000000009004021790555060608201518160020160106101000a81548161ffff021916908361ffff160217905550905050505050505050565b6060610ddd85858585610f46565b9050610de7610fd0565b608060405190810160405280600060010281526020016000600102815260200160007c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001600061ffff168152509050806001836040518082805190602001908083835b602083101515610e905780518252602082019150602081019050602083039250610e6b565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020600082015181600001556020820151816001015560408201518160020160006101000a81548163ffffffff02191690837c01000000000000000000000000000000000000000000000000000000009004021790555060608201518160020160046101000a81548161ffff021916908361ffff160217905550905050505050505050565b60608484848460405160200180858152602001848152602001837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020018261ffff1661ffff1681526020019450505050506040516020818303038152906040529050949350505050565b608060405190810160405280600080191681526020016000801916815260200160007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001600061ffff1681525090565b608060405190810160405280600080191681526020016000801916815260200160006fffffffffffffffffffffffffffffffff19168152602001600061ffff168152509056fea165627a7a723058204b7ab2c61abb56b4e5ce986bfa2e69bca17dd151f9afb000b381f6e804bccdd30029", + "storage": { + // ipv4 values + // ipv4 1 enode high + "0xa7f588badda5fb148b2b31c5257c51e80a11e6e73a3958e7b6b39a2560015af8": "0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012", + // ipv4 1 enode low + "0xa7f588badda5fb148b2b31c5257c51e80a11e6e73a3958e7b6b39a2560015af9": "0x937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0", + // ipv4 1 port and ip together + "0xa7f588badda5fb148b2b31c5257c51e80a11e6e73a3958e7b6b39a2560015afa": "0x765fc0a80001", + // ipv4 2 enode high + "0x5fcbab1eb48127703304543c4b2646c217e4d8d32d8a34e00e224551dcb256ad": "0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012", + // ipv4 2 enode low + "0x5fcbab1eb48127703304543c4b2646c217e4d8d32d8a34e00e224551dcb256ae": "0x937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0", + // ipv4 2 port and ip together + "0x5fcbab1eb48127703304543c4b2646c217e4d8d32d8a34e00e224551dcb256af": "0x00000000000000000000000000000000000000000000000000007660c0a80001", + + // ipv6 values + // ipv6 1 enode high + "0x1ab0d5b7c4004723d5020bf461a01709cbabee4aaf0b414f92d903f5779b33d9": "0x1234000000000000000000000000000000000000000000000000000000000000", + // ipv6 1 enode low + "0x1ab0d5b7c4004723d5020bf461a01709cbabee4aaf0b414f92d903f5779b33da": "0xab61", + // ipv6 1 port followed by ip + "0x1ab0d5b7c4004723d5020bf461a01709cbabee4aaf0b414f92d903f5779b33db": "0x765f00010002000300040005000600070008", + // ipv6 2 enode high + "0x12b025583b6b680e25edb31eb9fd6afae6c4b2c1d8c4c3beb1066be39601abc5": "0x1234000000000000000000000000000000000000000000000000000000000000", + // ipv6 2 enode low + "0x12b025583b6b680e25edb31eb9fd6afae6c4b2c1d8c4c3beb1066be39601abc6": "0x000000000000000000000000000000000000000000000000000000000000ab62", + // ipv6 2 port followed by ip + "0x12b025583b6b680e25edb31eb9fd6afae6c4b2c1d8c4c3beb1066be39601abc7": "0x0000000000000000000000000000766000010002000300040005000600070008" + } + } + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +}