Skip to content

Commit

Permalink
feat: save write ack event height (#734)
Browse files Browse the repository at this point in the history
* feat: save write ack event height

* chore: style fixes

Signed-off-by: Night Owl <nightowl121@protonmail.com>

* fix: add test case

---------

Signed-off-by: Night Owl <nightowl121@protonmail.com>
Co-authored-by: viveksharmapoudel <viveksharmapoudel@gmail.com>
Co-authored-by: Night Owl <nightowl121@protonmail.com>
  • Loading branch information
3 people authored Sep 20, 2023
1 parent 3c147a7 commit 6eb6a85
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ public void _sendPacket(Packet packet) {
Context.require(packet.getTimeoutTimestamp().equals(BigInteger.ZERO),
"Timeout timestamps are not available, use timeout height instead");


DictDB<String, BigInteger> nextSequenceSourcePort = nextSequenceSends.at(packet.getSourcePort());
BigInteger nextSequenceSend = nextSequenceSourcePort.getOrDefault(packet.getSourceChannel(), BigInteger.ZERO);
BigInteger nextSequenceSend = nextSequenceSourcePort.getOrDefault(packet.getSourceChannel(),
BigInteger.ZERO);
Context.require(
packet.getSequence().equals(nextSequenceSend),
"packet sequence != next send sequence");
Expand All @@ -59,14 +59,16 @@ public void _sendPacket(Packet packet) {

byte[] packetCommitment = createPacketCommitment(packet);
commitments.set(packetCommitmentKey, packetCommitment);
packetHeights.at(packet.getSourcePort()).at(packet.getSourceChannel()).set(packet.getSequence(), Context.getBlockHeight());
packetHeights.at(packet.getSourcePort()).at(packet.getSourceChannel()).set(packet.getSequence(),
Context.getBlockHeight());

sendBTPMessage(connection.getClientId(),
ByteUtil.join(packetCommitmentKey, packetCommitment));
}

public void _recvPacket(Packet packet, byte[] proof, byte[] proofHeight) {
Channel channel = Channel.decode(channels.at(packet.getDestinationPort()).get(packet.getDestinationChannel()));
Channel channel = Channel
.decode(channels.at(packet.getDestinationPort()).get(packet.getDestinationChannel()));
Context.require(channel.getState() == Channel.State.STATE_OPEN, "channel state must be OPEN");

Context.require(
Expand All @@ -85,7 +87,8 @@ public void _recvPacket(Packet packet, byte[] proof, byte[] proofHeight) {
Context.require(
packet.getTimeoutHeight().getRevisionHeight().equals(BigInteger.ZERO)
|| BigInteger.valueOf(Context.getBlockHeight())
.compareTo(packet.getTimeoutHeight().getRevisionHeight()) < 0,
.compareTo(packet.getTimeoutHeight()
.getRevisionHeight()) < 0,
"block height >= packet timeout height");
Context.require(
packet.getTimeoutTimestamp().equals(BigInteger.ZERO)
Expand Down Expand Up @@ -114,12 +117,14 @@ public void _recvPacket(Packet packet, byte[] proof, byte[] proofHeight) {
} else if (channel.getOrdering() == Channel.Order.ORDER_ORDERED) {
DictDB<String, BigInteger> nextSequenceDestinationPort = nextSequenceReceives
.at(packet.getDestinationPort());
BigInteger nextSequenceRecv = nextSequenceDestinationPort.getOrDefault(packet.getDestinationChannel(),
BigInteger nextSequenceRecv = nextSequenceDestinationPort.getOrDefault(
packet.getDestinationChannel(),
BigInteger.ZERO);
Context.require(
nextSequenceRecv.equals(packet.getSequence()),
"packet sequence != next receive sequence");
nextSequenceDestinationPort.set(packet.getDestinationChannel(), nextSequenceRecv.add(BigInteger.ONE));
nextSequenceDestinationPort.set(packet.getDestinationChannel(),
nextSequenceRecv.add(BigInteger.ONE));
} else {
Context.revert("unknown ordering type");
}
Expand All @@ -141,6 +146,9 @@ public void _writeAcknowledgement(String destinationPortId, String destinationCh
Context.require(commitments.get(ackCommitmentKey) == null, "acknowledgement for packet already exists");
byte[] ackCommitment = IBCCommitment.keccak256(acknowledgement);
commitments.set(ackCommitmentKey, ackCommitment);
ackHeights.at(destinationPortId).at(destinationChannel).set(sequence,
Context.getBlockHeight());

sendBTPMessage(connection.getClientId(), ByteUtil.join(ackCommitmentKey, ackCommitment));

}
Expand Down Expand Up @@ -216,7 +224,7 @@ public void _requestTimeout(MsgRequestTimeoutPacket msg) {
Context.require(connection.getState() == ConnectionEnd.State.STATE_OPEN,
"connection state is not OPEN");

BigInteger revisionHeight=packet.getTimeoutHeight().getRevisionHeight();
BigInteger revisionHeight = packet.getTimeoutHeight().getRevisionHeight();
boolean heightTimeout = revisionHeight.compareTo(BigInteger.ZERO) > 0
&& BigInteger.valueOf(Context.getBlockHeight())
.compareTo(revisionHeight) >= 0;
Expand Down Expand Up @@ -282,7 +290,8 @@ public void _timeoutPacket(Packet packet, byte[] proofHeight, byte[] proof, BigI
// check that timeout height or timeout timestamp has passed on the other end
Height height = Height.decode(proofHeight);
boolean heightTimeout = packet.getTimeoutHeight().getRevisionHeight().compareTo(BigInteger.ZERO) > 0
&& height.getRevisionHeight().compareTo(packet.getTimeoutHeight().getRevisionHeight()) >= 0;
&& height.getRevisionHeight()
.compareTo(packet.getTimeoutHeight().getRevisionHeight()) >= 0;
Context.require(heightTimeout, "Packet has not yet timed out");

// verify we actually sent this packet, check the store
Expand All @@ -304,8 +313,7 @@ public void _timeoutPacket(Packet packet, byte[] proofHeight, byte[] proof, BigI
connection,
proofHeight,
proof,
packetReceiptKey
);
packetReceiptKey);
} else if (channel.getOrdering() == Channel.Order.ORDER_ORDERED) {
// ordered channel: check that packet has not been received
// only allow timeout on next sequence so all sequences before the timed out
Expand All @@ -327,14 +335,16 @@ public void _timeoutPacket(Packet packet, byte[] proofHeight, byte[] proof, BigI
channel.setState(Channel.State.STATE_CLOSED);

byte[] encodedChannel = channel.encode();
updateChannelCommitment(connection.getClientId(), packet.getSourcePort(), packet.getSourceChannel(), encodedChannel);
updateChannelCommitment(connection.getClientId(), packet.getSourcePort(),
packet.getSourceChannel(), encodedChannel);
channels.at(packet.getSourcePort()).set(packet.getSourceChannel(), encodedChannel);
} else {
Context.revert("unknown ordering type");
}

commitments.set(packetCommitmentKey, null);
packetHeights.at(packet.getSourcePort()).at(packet.getSourceChannel()).set(packet.getSequence(), Context.getBlockHeight());
packetHeights.at(packet.getSourcePort()).at(packet.getSourceChannel()).set(packet.getSequence(),
Context.getBlockHeight());

}

Expand Down
34 changes: 27 additions & 7 deletions contracts/javascore/ibc/src/main/java/ibc/ics24/host/IBCStore.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package ibc.ics24.host;

import ibc.icon.interfaces.ILightClient;
import ibc.icon.interfaces.IIBCHost;
import ibc.icon.interfaces.ILightClient;
import ibc.icon.interfaces.ILightClientScoreInterface;
import ibc.icon.score.util.NullChecker;
import ibc.ics05.port.ModuleManager;
Expand All @@ -25,7 +25,7 @@ public abstract class IBCStore extends ModuleManager implements IIBCHost {
private static final String NEXT_SEQUENCE_RECEIVES = "nextSequenceReceives";
private static final String NEXT_SEQUENCE_ACKNOWLEDGEMENTS = "nextSequenceAcknowledgements";
private static final String PACKET_RECEIPTS = "packetReceipts";
private static final String PACKET_HEIGHTS= "packetHeights";
private static final String PACKET_HEIGHTS = "packetHeights";
private static final String CAPABILITIES = "capabilities";
private static final String PORT_IDS = "portIds";
private static final String EXPECTED_TIME_PER_BLOCK = "expectedTimePerBlock";
Expand All @@ -34,6 +34,7 @@ public abstract class IBCStore extends ModuleManager implements IIBCHost {
private static final String NEXT_CHANNEL_SEQUENCE = "nextChannelSequence";
private static final String BTP_NETWORK_ID = "btpNetworkId";
private static final String TIMEOUT_REQUESTS = "timeout_requests";
private static final String ACK_HEIGHTS = "ackHeights";

// DB Variables
// Commitments
Expand Down Expand Up @@ -61,17 +62,21 @@ public abstract class IBCStore extends ModuleManager implements IIBCHost {
.newBranchDB(PACKET_RECEIPTS, Boolean.class);
public static final BranchDB<String, BranchDB<String, DictDB<BigInteger, Long>>> packetHeights = Context
.newBranchDB(PACKET_HEIGHTS, Long.class);
public static final BranchDB<String, BranchDB<String, DictDB<BigInteger, Long>>> ackHeights = Context
.newBranchDB(ACK_HEIGHTS, Long.class);

public static final DictDB<byte[],Address> capabilities = Context.newDictDB(CAPABILITIES, Address.class);
public static final DictDB<byte[], Address> capabilities = Context.newDictDB(CAPABILITIES, Address.class);
public static final ArrayDB<String> portIds = Context.newArrayDB(PORT_IDS, String.class);
// Host Parameters
public static final VarDB<BigInteger> expectedTimePerBlock = Context.newVarDB(EXPECTED_TIME_PER_BLOCK, BigInteger.class);
public static final VarDB<BigInteger> expectedTimePerBlock = Context.newVarDB(EXPECTED_TIME_PER_BLOCK,
BigInteger.class);

// Sequences for identifiers
public static final VarDB<BigInteger> nextClientSequence = Context.newVarDB(NEXT_CLIENT_SEQUENCE, BigInteger.class);
public static final VarDB<BigInteger> nextConnectionSequence = Context.newVarDB(NEXT_CONNECTION_SEQUENCE,
BigInteger.class);
public static final VarDB<BigInteger> nextChannelSequence = Context.newVarDB(NEXT_CHANNEL_SEQUENCE, BigInteger.class);
public static final VarDB<BigInteger> nextChannelSequence = Context.newVarDB(NEXT_CHANNEL_SEQUENCE,
BigInteger.class);

public static final DictDB<String, Integer> btpNetworkId = Context.newDictDB(BTP_NETWORK_ID, Integer.class);
public static final DictDB<byte[], Boolean> timeoutRequests = Context.newDictDB(TIMEOUT_REQUESTS, Boolean.class);
Expand Down Expand Up @@ -123,7 +128,7 @@ public BigInteger getNextSequenceAcknowledgement(String portId, String channelId

@External(readonly = true)
public boolean getPacketReceipt(String portId, String channelId, BigInteger sequence) {
return packetReceipts.at(portId).at(channelId).getOrDefault(sequence, false);
return packetReceipts.at(portId).at(channelId).getOrDefault(sequence, false);
}

@External(readonly = true)
Expand Down Expand Up @@ -189,7 +194,22 @@ public Map<String, Long> getPacketHeights(String portId, String channelId, int s
for (int i = startSequence; i <= endSequence; i++) {
BigInteger sequence = BigInteger.valueOf(i);
Long height = packets.get(sequence);
if (height != null){
if (height != null) {
heights.put(sequence.toString(), height);
}
}

return heights;
}

@External(readonly = true)
public Map<String, Long> getAckHeights(String portId, String channelId, int startSequence, int endSequence) {
DictDB<BigInteger, Long> acks = ackHeights.at(portId).at(channelId);
Map<String, Long> heights = new HashMap<>();
for (int i = startSequence; i <= endSequence; i++) {
BigInteger sequence = BigInteger.valueOf(i);
Long height = acks.get(sequence);
if (height != null) {
heights.put(sequence.toString(), height);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import java.math.BigInteger;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
Expand Down Expand Up @@ -100,6 +101,23 @@ void sendAndAckPacket() throws Exception {
acknowledgePacket();
}

@Test
@SuppressWarnings("unchecked")
void ackResponse_checkAckHeightSaved() throws Exception{

establishCommunication();

receivePacket();
Map<String, Long> ackHeights = (Map<String, Long>) handler.call("getAckHeights", portId, channelId, 0,10);
assertEquals( ackHeights.keySet().size(), 0);


writeAcknowledgement();
ackHeights = (Map<String, Long>) handler.call("getAckHeights", portId, channelId, 0, 10);
assertEquals( ackHeights.keySet().size(), 1);
assertTrue(ackHeights.get("1") !=null, "ack height is not saved properly" );
}

@Test
void sendAndTimeoutPacket() throws Exception {
establishCommunication();
Expand Down

0 comments on commit 6eb6a85

Please sign in to comment.