diff --git a/contracts/javascore/ibc/src/intTest/java/ibc/core/integration/IBCIntegrationTest.java b/contracts/javascore/ibc/src/intTest/java/ibc/core/integration/IBCIntegrationTest.java index 40836db6c..0dba5f8b8 100644 --- a/contracts/javascore/ibc/src/intTest/java/ibc/core/integration/IBCIntegrationTest.java +++ b/contracts/javascore/ibc/src/intTest/java/ibc/core/integration/IBCIntegrationTest.java @@ -64,7 +64,7 @@ static void setup() throws Exception { @Test @Order(0) void registerClient() { - getClientInterface(owner).registerClient(clientType, mockLightClient._address()); + getClientInterface(owner).registerClient(clientType, mockLightClient._address(), 0); } @Test diff --git a/contracts/javascore/ibc/src/main/java/ibc/ics03/connection/IBCConnection.java b/contracts/javascore/ibc/src/main/java/ibc/ics03/connection/IBCConnection.java index 147917c5e..211e75585 100644 --- a/contracts/javascore/ibc/src/main/java/ibc/ics03/connection/IBCConnection.java +++ b/contracts/javascore/ibc/src/main/java/ibc/ics03/connection/IBCConnection.java @@ -1,17 +1,18 @@ package ibc.ics03.connection; import java.math.BigInteger; -import java.util.Arrays; import java.util.List; import icon.proto.core.client.Height; import icon.proto.core.connection.ConnectionEnd; import icon.proto.core.connection.Counterparty; import icon.proto.core.connection.Version; +import ibc.core.commitment.v1.MerklePrefix; import ibc.icon.score.util.ByteUtil; import ibc.icon.score.util.Logger; import ibc.ics02.client.IBCClient; import ibc.ics24.host.IBCCommitment; +import ibc.ics24.host.IBCHost; import icon.ibc.interfaces.ILightClient; import icon.ibc.structs.messages.MsgConnectionOpenAck; import icon.ibc.structs.messages.MsgConnectionOpenConfirm; @@ -24,6 +25,12 @@ public class IBCConnection extends IBCClient { public static final String v1Identifier = "1"; public static final List supportedV1Features = List.of("ORDER_ORDERED", "ORDER_UNORDERED"); + public static final MerklePrefix ICS08PREFIX = new MerklePrefix(); + static { + ICS08PREFIX.setKeyPrefix("ibc".getBytes()); + + } + Logger logger = new Logger("ibc-core"); public String _connectionOpenInit(MsgConnectionOpenInit msg) { @@ -67,6 +74,9 @@ public String _connectionOpenTry(MsgConnectionOpenTry msg) { Counterparty expectedCounterparty = new Counterparty(); expectedCounterparty.setClientId(msg.getClientId()); expectedCounterparty.setConnectionId(""); + if (IBCCommitment.getHashType(msg.getClientId()) == IBCHost.HashType.ICS08.type) { + expectedCounterparty.setPrefix(ICS08PREFIX); + } ConnectionEnd expectedConnection = new ConnectionEnd(); expectedConnection.setClientId(counterparty.getClientId()); @@ -108,6 +118,9 @@ public byte[] _connectionOpenAck(MsgConnectionOpenAck msg) { Counterparty expectedCounterparty = new Counterparty(); expectedCounterparty.setClientId(connection.getClientId()); expectedCounterparty.setConnectionId(msg.getConnectionId()); + if (IBCCommitment.getHashType(connection.getClientId()) == IBCHost.HashType.ICS08.type) { + expectedCounterparty.setPrefix(ICS08PREFIX); + } ConnectionEnd expectedConnection = new ConnectionEnd(); expectedConnection.setClientId(connection.getCounterparty().getClientId()); @@ -150,6 +163,9 @@ public byte[] _connectionOpenConfirm(MsgConnectionOpenConfirm msg) { Counterparty expectedCounterparty = new Counterparty(); expectedCounterparty.setClientId(connection.getClientId()); expectedCounterparty.setConnectionId(msg.getConnectionId()); + if (IBCCommitment.getHashType(connection.getClientId()) == IBCHost.HashType.ICS08.type) { + expectedCounterparty.setPrefix(ICS08PREFIX); + } ConnectionEnd expectedConnection = new ConnectionEnd(); expectedConnection.setClientId(connection.getCounterparty().getClientId()); diff --git a/contracts/javascore/ibc/src/main/java/ibc/ics04/channel/IBCPacket.java b/contracts/javascore/ibc/src/main/java/ibc/ics04/channel/IBCPacket.java index 3853c845e..08812678e 100644 --- a/contracts/javascore/ibc/src/main/java/ibc/ics04/channel/IBCPacket.java +++ b/contracts/javascore/ibc/src/main/java/ibc/ics04/channel/IBCPacket.java @@ -181,12 +181,13 @@ public void _acknowledgePacket(Packet packet, byte[] acknowledgement, byte[] pro byte[] packetAckPath = IBCCommitment.packetAcknowledgementCommitmentPath(packet.getDestinationPort(), packet.getDestinationChannel(), packet.getSequence()); + byte[] commitmentBytes = createAcknowledgmentCommitmentBytes(connection.getClientId(), acknowledgement); verifyPacketAcknowledgement( connection, proofHeight, proof, packetAckPath, - acknowledgement); + commitmentBytes); if (channel.getOrdering() == Channel.Order.ORDER_ORDERED) { DictDB nextSequenceAckSourcePort = nextSequenceAcknowledgements diff --git a/contracts/javascore/ibc/src/main/java/ibc/ics23/commitment/types/Merkle.java b/contracts/javascore/ibc/src/main/java/ibc/ics23/commitment/types/Merkle.java index bcd73dc45..8efd8d4a0 100644 --- a/contracts/javascore/ibc/src/main/java/ibc/ics23/commitment/types/Merkle.java +++ b/contracts/javascore/ibc/src/main/java/ibc/ics23/commitment/types/Merkle.java @@ -20,7 +20,7 @@ public class Merkle { Proof.getTendermintSpec() ); - public static MerklePath applyPrefix(String path, String prefix) { + public static MerklePath applyPrefix(String prefix, String path) { var mpath = new MerklePath(); List keyPath = new ArrayList<>(); keyPath.add(prefix); diff --git a/contracts/javascore/lightclients/ics-08-tendermint/src/main/java/ibc/ics08/tendermint/TendermintHelper.java b/contracts/javascore/lightclients/ics-08-tendermint/src/main/java/ibc/ics08/tendermint/TendermintHelper.java index 229566a47..ce8c4a809 100644 --- a/contracts/javascore/lightclients/ics-08-tendermint/src/main/java/ibc/ics08/tendermint/TendermintHelper.java +++ b/contracts/javascore/lightclients/ics-08-tendermint/src/main/java/ibc/ics08/tendermint/TendermintHelper.java @@ -58,7 +58,7 @@ public static BigInteger getTotalVotingPower(ValidatorSet validatorSet) { } public static BigInteger getRevisionNumber(String chainId) { - int id = chainId.indexOf("-"); + int id = chainId.lastIndexOf("-"); if (id >= 0) { return new BigInteger(chainId.substring(id+1)); } diff --git a/contracts/javascore/lightclients/tendermint/src/main/java/ibc/tendermint/TendermintLightClient.java b/contracts/javascore/lightclients/tendermint/src/main/java/ibc/tendermint/TendermintLightClient.java index 7e1b0be41..aff686b6f 100644 --- a/contracts/javascore/lightclients/tendermint/src/main/java/ibc/tendermint/TendermintLightClient.java +++ b/contracts/javascore/lightclients/tendermint/src/main/java/ibc/tendermint/TendermintLightClient.java @@ -213,7 +213,7 @@ public void verifyMembership( var root = consensusState.getRoot(); var merkleProof = MerkleProof.decode(proof); - var merklePath = applyPrefix(new String(path), StringUtil.bytesToHex("wasm".getBytes())); + var merklePath = applyPrefix(StringUtil.bytesToHex("wasm".getBytes()), new String(path)); Merkle.verifyMembership(merkleProof, Merkle.SDK_SPEC, root, merklePath, value); } @@ -240,7 +240,7 @@ public void verifyNonMembership( var root = consensusState.getRoot(); var merkleProof = MerkleProof.decode(proof); - var merklePath = applyPrefix(new String(path), StringUtil.bytesToHex("wasm".getBytes())); + var merklePath = applyPrefix(StringUtil.bytesToHex("wasm".getBytes()), new String(path)); Merkle.verifyNonMembership(merkleProof, Merkle.SDK_SPEC, root, merklePath); } diff --git a/contracts/javascore/score-util/src/main/java/ibc/icon/score/util/Proto.java b/contracts/javascore/score-util/src/main/java/ibc/icon/score/util/Proto.java index 740e21a52..0f176b3fa 100644 --- a/contracts/javascore/score-util/src/main/java/ibc/icon/score/util/Proto.java +++ b/contracts/javascore/score-util/src/main/java/ibc/icon/score/util/Proto.java @@ -170,7 +170,7 @@ public static byte[] encodeBooleanArray(int order, List items) { } public static byte[] encode(int order, Boolean item) { - if (item == null) { + if (item == null || item == false) { return new byte[0]; } diff --git a/proto/clients/tendermint/TendermintLight.proto b/proto/clients/tendermint/TendermintLight.proto new file mode 100644 index 000000000..1c802d95c --- /dev/null +++ b/proto/clients/tendermint/TendermintLight.proto @@ -0,0 +1,206 @@ +syntax = "proto3"; +package tendermint.light; + +option go_package = "libraries/go/common/tendermint;tendermint"; +import "gogoproto/gogo.proto"; + +message Fraction { + uint64 numerator = 1; + uint64 denominator = 2; +} + +// https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp +message Duration { + int64 seconds = 1; + int32 nanos = 2; +} + +message Consensus { + uint64 block = 1; + uint64 app = 2; +} + +message ClientState { + option (gogoproto.goproto_getters) = false; + + string chain_id = 1; + Fraction trust_level = 2; + + // duration of the period since the LastestTimestamp during which the + // submitted headers are valid for upgrade + Duration trusting_period = 3; + // duration of the staking unbonding period + Duration unbonding_period = 4; + // defines how much new (untrusted) header's Time can drift into the future. + Duration max_clock_drift = 5; + // Block height when the client was frozen due to a misbehaviour + //ibc.core.client.v1.Height frozen_height = 6; + int64 frozen_height = 6; + // Latest height the client was updated to + int64 latest_height = 7; + // This flag, when set to true, will allow governance to recover a client + // which has expired + bool allow_update_after_expiry = 8; + // This flag, when set to true, will allow governance to unfreeze a client + // whose chain has experienced a misbehaviour event + bool allow_update_after_misbehaviour = 9; +} + +// ConsensusState defines the consensus state from Tendermint. +message ConsensusState { + option (gogoproto.goproto_getters) = false; + // timestamp that corresponds to the block height in which the ConsensusState + // was stored. + Timestamp timestamp = 1; + + // commitment root (i.e app hash) + MerkleRoot root = 2; + bytes next_validators_hash = 3; +} + +// MerkleRoot defines a merkle root hash. +// In the Cosmos SDK, the AppHash of a block header becomes the root. +message MerkleRoot { + bytes hash = 1; +} + +enum BlockIDFlag { + BLOCK_ID_FLAG_UNKNOWN = 0; + BLOCK_ID_FLAG_ABSENT = 1; + BLOCK_ID_FLAG_COMMIT = 2; + BLOCK_ID_FLAG_NIL = 3; +} + +enum SignedMsgType { + SIGNED_MSG_TYPE_UNKNOWN = 0; + // Votes + SIGNED_MSG_TYPE_PREVOTE = 1; + SIGNED_MSG_TYPE_PRECOMMIT = 2; + + // Proposals + SIGNED_MSG_TYPE_PROPOSAL = 32; +} + +message CanonicalPartSetHeader { + uint32 total = 1; + bytes hash = 2; +} + +message CanonicalBlockID { + bytes hash = 1; + CanonicalPartSetHeader part_set_header = 2; +} + +message CanonicalVote { + SignedMsgType type = 1; + sfixed64 height = 2; + sfixed64 round = 3; + BlockID block_id = 4; + Timestamp timestamp = 5; + string chain_id = 6; +} + +message Vote { + SignedMsgType type = 1; + int64 height = 2; + int32 round = 3; + BlockID block_id = 4; + Timestamp timestamp = 5; + bytes validator_address = 6; + int32 validator_index = 7; + bytes signature = 8; +} + +message ValidatorSet { + repeated Validator validators = 1; + Validator proposer = 2; + int64 total_voting_power = 3; +} + +message Validator { + bytes address = 1; + PublicKey pub_key = 2; + int64 voting_power = 3; + int64 proposer_priority = 4; +} + +message SimpleValidator { + PublicKey pub_key = 1; + int64 voting_power = 2; +} + +message PublicKey { + oneof sum { + bytes ed25519 = 1; + bytes secp256k1 = 2; + bytes sr25519 = 3; + } +} + +message PartSetHeader { + uint32 total = 1; + bytes hash= 2; +} + +message BlockID { + bytes hash = 1; + PartSetHeader part_set_header = 2; +} + +message Commit { + int64 height = 1; + int32 round = 2; + BlockID block_id = 3; + repeated CommitSig signatures = 4; +} + +// CommitSig is a part of the Vote included in a Commit. +message CommitSig { + BlockIDFlag block_id_flag = 1; + bytes validator_address = 2; + Timestamp timestamp = 3; + bytes signature = 4; +} + +message Timestamp { + // Represents seconds of UTC time since Unix epoch + // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + // 9999-12-31T23:59:59Z inclusive. + int64 seconds = 1; + + // Non-negative fractions of a second at nanosecond resolution. Negative + // second values with fractions must still have non-negative nanos values + // that count forward in time. Must be from 0 to 999,999,999 + // inclusive. + int32 nanos = 2; +} + +message LightHeader { + Consensus version = 1; + string chain_id = 2; + int64 height = 3; + Timestamp time = 4; + BlockID last_block_id = 5; + bytes last_commit_hash = 6; // commit from validators from the last block + bytes data_hash = 7; // transactions + bytes validators_hash = 8; // validators for the current block + bytes next_validators_hash = 9; // validators for the next block + bytes consensus_hash = 10; // consensus params for current block + bytes app_hash = 11; // state after txs from the previous block + bytes last_results_hash = 12; // root hash of all results from the txs from the previous block + bytes evidence_hash = 13; // evidence included in the block + bytes proposer_address = 14; // original proposer of the block +} + +message SignedHeader { + LightHeader header = 1; + Commit commit = 2; +} + +message TmHeader { + SignedHeader signed_header = 1; + ValidatorSet validator_set = 2; + + int64 trusted_height = 3; + ValidatorSet trusted_validators = 4; +}