diff --git a/README.md b/README.md index 7176e87a..ee1f5c4c 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ previous blocks, query information from a local node, monitor the local mempool, - [0.2.5](https://github.com/bloxbean/yaci/releases/tag/v0.2.5) (Compatible with Cardano Client Lib 0.5.0) **Latest Release :** -- [0.3.0-beta15](https://github.com/bloxbean/yaci/releases/tag/v0.3.0-beta15) (Supports Conway era) +- [0.3.2](https://github.com/bloxbean/yaci/releases/tag/v0.3.2) (Supports Conway era) **Development Branch:** main diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/BlockSerializer.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/BlockSerializer.java index b3bf4daf..07e8ffb6 100644 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/BlockSerializer.java +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/BlockSerializer.java @@ -230,7 +230,7 @@ private void handleWitnessDatumRedeemer(long block, List witnesses, b continue; } - var actualRedeemerData = redeemerFields.get(1); + var actualRedeemerData = redeemerFields.get(0); var redeemerData = redeemer.getData(); final var cbor = HexUtil.encodeHexString(actualRedeemerData); final var hash = Datum.cborToHash(actualRedeemerData); diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/governance/GovActionIdSerializer.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/governance/GovActionIdSerializer.java index 61327416..b6c5ad57 100644 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/governance/GovActionIdSerializer.java +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/model/serializers/governance/GovActionIdSerializer.java @@ -4,6 +4,7 @@ import co.nstant.in.cbor.model.ByteString; import co.nstant.in.cbor.model.DataItem; import co.nstant.in.cbor.model.UnsignedInteger; +import com.bloxbean.cardano.client.util.StringUtils; import com.bloxbean.cardano.yaci.core.model.governance.GovActionId; import com.bloxbean.cardano.yaci.core.protocol.Serializer; import com.bloxbean.cardano.yaci.core.util.HexUtil; @@ -15,6 +16,22 @@ public enum GovActionIdSerializer implements Serializer { INSTANCE; + @Override + public DataItem serializeDI(GovActionId govActionId) { + Array array = new Array(); + + if (StringUtils.isEmpty(govActionId.getTransactionId()) || govActionId.getGov_action_index() == null) { + log.error("Invalid gov_action_id : {}", govActionId); + throw new IllegalArgumentException("Invalid gov_action_id, " + + "expect transaction_id and gov_action_index not null and not empty"); + } + + array.add(new ByteString(HexUtil.decodeHexString(govActionId.getTransactionId()))); + array.add(new UnsignedInteger(govActionId.getGov_action_index())); + + return array; + } + @Override public GovActionId deserializeDI(DataItem di) { Array actionIdArray = (Array) di; @@ -28,4 +45,5 @@ public GovActionId deserializeDI(DataItem di) { return new GovActionId(txId, govActionIndex); } + } diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/handshake/util/N2CVersionTableConstant.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/handshake/util/N2CVersionTableConstant.java index afa3fbd8..0b6914af 100644 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/handshake/util/N2CVersionTableConstant.java +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/handshake/util/N2CVersionTableConstant.java @@ -25,6 +25,7 @@ public class N2CVersionTableConstant { public final static long PROTOCOL_V14 = 32782; public final static long PROTOCOL_V15 = 32783; public final static long PROTOCOL_V16 = 32784; + public final static long PROTOCOL_V17 = 32785; public static VersionTable v1AndAbove(long networkMagic) { OldN2CVersionData oldVersionData = new OldN2CVersionData(networkMagic); @@ -47,6 +48,7 @@ public static VersionTable v1AndAbove(long networkMagic) { versionTableMap.put(PROTOCOL_V14, oldVersionData); versionTableMap.put(PROTOCOL_V15, versionData); versionTableMap.put(PROTOCOL_V16, versionData); + versionTableMap.put(PROTOCOL_V17, versionData); return new VersionTable(versionTableMap); } diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GetProposalQueryResult.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GetProposalQueryResult.java new file mode 100644 index 00000000..640b855c --- /dev/null +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GetProposalQueryResult.java @@ -0,0 +1,16 @@ +package com.bloxbean.cardano.yaci.core.protocol.localstate.queries; + +import com.bloxbean.cardano.yaci.core.protocol.localstate.api.QueryResult; +import com.bloxbean.cardano.yaci.core.protocol.localstate.queries.model.Proposal; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +import java.util.List; + +@Getter +@ToString +@AllArgsConstructor +public class GetProposalQueryResult implements QueryResult { + private List proposals; +} diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GetProposalsQuery.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GetProposalsQuery.java new file mode 100644 index 00000000..522b5370 --- /dev/null +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GetProposalsQuery.java @@ -0,0 +1,157 @@ +package com.bloxbean.cardano.yaci.core.protocol.localstate.queries; + +import co.nstant.in.cbor.model.*; +import com.bloxbean.cardano.yaci.core.model.Credential; +import com.bloxbean.cardano.yaci.core.model.certs.StakeCredType; +import com.bloxbean.cardano.yaci.core.model.certs.StakePoolId; +import com.bloxbean.cardano.yaci.core.model.governance.Drep; +import com.bloxbean.cardano.yaci.core.model.governance.GovActionId; +import com.bloxbean.cardano.yaci.core.model.governance.ProposalProcedure; +import com.bloxbean.cardano.yaci.core.model.governance.Vote; +import com.bloxbean.cardano.yaci.core.model.serializers.governance.AnchorSerializer; +import com.bloxbean.cardano.yaci.core.model.serializers.governance.GovActionIdSerializer; +import com.bloxbean.cardano.yaci.core.protocol.handshake.messages.AcceptVersion; +import com.bloxbean.cardano.yaci.core.protocol.localstate.api.Era; +import com.bloxbean.cardano.yaci.core.protocol.localstate.api.EraQuery; +import com.bloxbean.cardano.yaci.core.protocol.localstate.queries.model.Proposal; +import com.bloxbean.cardano.yaci.core.util.HexUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import static com.bloxbean.cardano.yaci.core.util.CborSerializationUtil.toBigInteger; +import static com.bloxbean.cardano.yaci.core.util.CborSerializationUtil.toInt; + +@Getter +@AllArgsConstructor +@ToString +public class GetProposalsQuery implements EraQuery { + private Era era; + private List govActionIds; + + public GetProposalsQuery() { + this.era = Era.Conway; + } + + public GetProposalsQuery(List govActionIds) { + this.era = Era.Conway; + this.govActionIds = govActionIds; + } + + @Override + public DataItem serialize(AcceptVersion protocolVersion) { + Array array = new Array(); + array.add(new UnsignedInteger(31)); + + Array govActionIdArray = new Array(); + + govActionIds.forEach(govActionId -> govActionIdArray.add(GovActionIdSerializer.INSTANCE.serializeDI(govActionId))); + + array.add(govActionIdArray); + + return wrapWithOuterArray(array); + } + + @Override + public GetProposalQueryResult deserializeResult(AcceptVersion protocolVersion, DataItem[] di) { + List proposals = new ArrayList<>(); + var proposalsDI = extractResultArray(di[0]); + var proposalArray = (Array) proposalsDI.get(0); + + for (DataItem item : proposalArray.getDataItems()) { + if (item == SimpleValue.BREAK) { + continue; + } + Proposal proposal = deserializeProposalResult(item); + proposals.add(proposal); + } + + return new GetProposalQueryResult(proposals); + } + + private Proposal deserializeProposalResult(DataItem proposalDI) { + Array proposalArray = (Array) proposalDI; + GovActionId govActionId = deserializeGovActionIdResult(proposalArray.getDataItems().get(0)); + + var proposalProcedureDI = (Array) proposalArray.getDataItems().get(4); + + ProposalProcedure proposalProcedure = ProposalProcedure.builder() + .anchor(AnchorSerializer.INSTANCE.deserializeDI(proposalProcedureDI.getDataItems().get(3))) + .rewardAccount(HexUtil.encodeHexString(((ByteString) proposalProcedureDI.getDataItems().get(1)).getBytes())) + // TODO: 'govAction' field + .deposit(toBigInteger(proposalProcedureDI.getDataItems().get(0))) + .build(); + + // committee votes + java.util.Map committeeVotes = new HashMap<>(); + var committeeVotesDI = (Map) proposalArray.getDataItems().get(1); + + for (DataItem key : committeeVotesDI.getKeys()) { + var credentialDI = (Array) key; + int credType = toInt(credentialDI.getDataItems().get(0)); + String credHash = HexUtil.encodeHexString(((ByteString) credentialDI.getDataItems().get(1)).getBytes()); + var voteDI = committeeVotesDI.get(credentialDI); + Vote vote = Vote.values()[toInt(voteDI)]; + committeeVotes.put(Credential.builder() + .type(credType == 0 ? StakeCredType.ADDR_KEYHASH : StakeCredType.SCRIPTHASH) + .hash(credHash) + .build(), vote); + } + + // dRep votes + java.util.Map dRepVotes = new HashMap<>(); + var dRepVotesDI = (Map) proposalArray.getDataItems().get(2); + + for (DataItem key : dRepVotesDI.getKeys()) { + var credentialDI = (Array) key; + int credType = toInt(credentialDI.getDataItems().get(0)); + String credHash = HexUtil.encodeHexString(((ByteString) credentialDI.getDataItems().get(1)).getBytes()); + var voteDI = dRepVotesDI.get(credentialDI); + Vote vote = Vote.values()[toInt(voteDI)]; + dRepVotes.put(credType == 0 ? Drep.addrKeyHash(credHash) : Drep.scriptHash(credHash), vote); + } + + // stake pool votes + java.util.Map stakePoolVotes = new HashMap<>(); + var stakePoolVotesDI = (Map) proposalArray.getDataItems().get(3); + for (DataItem key : stakePoolVotesDI.getKeys()) { + String poolHash = HexUtil.encodeHexString(((ByteString) key).getBytes()); + var voteDI = stakePoolVotesDI.get(key); + Vote vote = Vote.values()[toInt(voteDI)]; + stakePoolVotes.put(StakePoolId.builder().poolKeyHash(poolHash).build(), vote); + } + + // expiredAfter + Integer expiredAfter = toInt(proposalArray.getDataItems().get(6)); + // proposedIn + Integer proposedIn = toInt(proposalArray.getDataItems().get(5)); + + return Proposal.builder() + .govActionId(govActionId) + .committeeVotes(committeeVotes) + .dRepVotes(dRepVotes) + .stakePoolVotes(stakePoolVotes) + .proposalProcedure(proposalProcedure) + .expiredAfter(expiredAfter) + .proposedIn(proposedIn) + .build(); + } + + private GovActionId deserializeGovActionIdResult(DataItem govActionId) { + Array govActionIdDI = (Array) govActionId; + + if (govActionIdDI.getDataItems().isEmpty()) { + return null; + } + + return GovActionId.builder() + .transactionId(HexUtil.encodeHexString(((ByteString) govActionIdDI.getDataItems().get(0)).getBytes())) + .gov_action_index(toInt(govActionIdDI.getDataItems().get(1))) + .build(); + } + +} diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GetRatifyStateQuery.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GetRatifyStateQuery.java new file mode 100644 index 00000000..fe3404c1 --- /dev/null +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GetRatifyStateQuery.java @@ -0,0 +1,539 @@ +package com.bloxbean.cardano.yaci.core.protocol.localstate.queries; + +import co.nstant.in.cbor.model.*; +import com.bloxbean.cardano.client.util.Tuple; +import com.bloxbean.cardano.yaci.core.model.Credential; +import com.bloxbean.cardano.yaci.core.model.DrepVoteThresholds; +import com.bloxbean.cardano.yaci.core.model.PoolVotingThresholds; +import com.bloxbean.cardano.yaci.core.model.ProtocolParamUpdate; +import com.bloxbean.cardano.yaci.core.model.certs.StakeCredType; +import com.bloxbean.cardano.yaci.core.model.certs.StakePoolId; +import com.bloxbean.cardano.yaci.core.model.governance.*; +import com.bloxbean.cardano.yaci.core.model.serializers.governance.AnchorSerializer; +import com.bloxbean.cardano.yaci.core.protocol.handshake.messages.AcceptVersion; +import com.bloxbean.cardano.yaci.core.protocol.localstate.api.Era; +import com.bloxbean.cardano.yaci.core.protocol.localstate.api.EraQuery; +import com.bloxbean.cardano.yaci.core.protocol.localstate.queries.model.EnactState; +import com.bloxbean.cardano.yaci.core.protocol.localstate.queries.model.Proposal; +import com.bloxbean.cardano.yaci.core.protocol.localstate.queries.model.ProposalType; +import com.bloxbean.cardano.yaci.core.protocol.localstate.queries.model.RatifyState; +import com.bloxbean.cardano.yaci.core.util.CborSerializationUtil; +import com.bloxbean.cardano.yaci.core.util.HexUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; + +import static com.bloxbean.cardano.yaci.core.util.CborSerializationUtil.*; +import static com.bloxbean.cardano.yaci.core.util.CborSerializationUtil.toRationalNumber; + +@Getter +@AllArgsConstructor +@ToString +public class GetRatifyStateQuery implements EraQuery { + private Era era; + + public GetRatifyStateQuery() { + this.era = Era.Conway; + } + + @Override + public DataItem serialize(AcceptVersion protocolVersion) { + Array queryArray = new Array(); + queryArray.add(new UnsignedInteger(32)); + + return wrapWithOuterArray(queryArray); + } + + @Override + public GetRatifyStateQueryResult deserializeResult(AcceptVersion protocolVersion, DataItem[] di) { + var ratifyStateResultDI = extractResultArray(di[0]); + var ratifyStateArray = (Array) ratifyStateResultDI.get(0); + + //ratify state - enacted gov actions + List enactedProposals = new ArrayList<>(); + Array enactedProposalArr = (Array) ratifyStateArray.getDataItems().get(1); + for (var proposalDI : enactedProposalArr.getDataItems()) { + Proposal enactedProposal = deserializeProposalResult(proposalDI); + enactedProposals.add(enactedProposal); + } + + // ratify state - expired gov actions + List expiredGovActions = deserializeGovActionIdListResult(ratifyStateArray.getDataItems().get(2)); + + // ratify state - next enact state + var nextEnactStateDI = (Array)ratifyStateArray.getDataItems().get(0); + + // next enact state - committee + var nextEnactStateCommitteeArr = (Array) nextEnactStateDI.getDataItems().get(0); + Committee nextEnactStateCommittee = deserializeCommitteeResult( + ((Array)nextEnactStateCommitteeArr.getDataItems().get(0)).getDataItems()); + + // next enact state - constitution + var nextEnactStateConstitutionArr = (Array)nextEnactStateDI.getDataItems().get(1); + Constitution nextEnactStateConstitution = deserializeConstitutionResult(nextEnactStateConstitutionArr.getDataItems().get(0)); + + // next enact state - current protocol params + ProtocolParamUpdate nextEnactStateCurrentPParams = deserializePPResult( + ((Array)nextEnactStateDI.getDataItems().get(2)).getDataItems()); + // next enact state - prev protocol params + ProtocolParamUpdate nextEnactStatePrevPParams = deserializePPResult( + ((Array)nextEnactStateDI.getDataItems().get(3)).getDataItems()); + // next enact state - Prev govActionIds + java.util.Map prevGovActionIds = new HashMap<>(); + Array nextEnactStatePrevGovActionIds = (Array) nextEnactStateDI.getDataItems().get(6); + + prevGovActionIds.put(ProposalType.COMMITTEE, + deserializeGovActionIdListResult(nextEnactStatePrevGovActionIds.getDataItems().get(0)) + .stream() + .findFirst() + .orElse(null)); + + prevGovActionIds.put(ProposalType.HARD_FORK, + deserializeGovActionIdListResult(nextEnactStatePrevGovActionIds.getDataItems().get(1)) + .stream() + .findFirst() + .orElse(null)); + + prevGovActionIds.put(ProposalType.CONSTITUTION, + deserializeGovActionIdListResult(nextEnactStatePrevGovActionIds.getDataItems().get(1)) + .stream() + .findFirst() + .orElse(null)); + + prevGovActionIds.put(ProposalType.P_PARAM_UPDATE, + deserializeGovActionIdListResult(nextEnactStatePrevGovActionIds.getDataItems().get(1)) + .stream() + .findFirst() + .orElse(null)); + + // ratify state - ratificationDelayed + var ratificationDelayedDI = ratifyStateArray.getDataItems().get(3); + Boolean ratificationDelayed = ratificationDelayedDI != null ? + (((SimpleValue) ratificationDelayedDI).getValue() == SimpleValueType.FALSE.getValue() ? Boolean.FALSE : Boolean.TRUE) + : null; + + RatifyState ratifyState = RatifyState.builder() + .ratificationDelayed(ratificationDelayed) + .nextEnactState(EnactState.builder() + .committee(nextEnactStateCommittee) + .constitution(nextEnactStateConstitution) + .currentPParams(nextEnactStateCurrentPParams) + .prevGovActionIds(prevGovActionIds) + .prevPParams(nextEnactStatePrevPParams) + .build()) + .enactedGovActions(enactedProposals) + .expiredGovActions(expiredGovActions) + .build(); + + return new GetRatifyStateQueryResult(ratifyState); + } + + public Proposal deserializeProposalResult(DataItem proposalDI) { + Array proposalArray = (Array) proposalDI; + GovActionId govActionId = deserializeGovActionIdResult(proposalArray.getDataItems().get(0)); + + var proposalProcedureDI = (Array) proposalArray.getDataItems().get(4); + + ProposalProcedure proposalProcedure = ProposalProcedure.builder() + .anchor(AnchorSerializer.INSTANCE.deserializeDI(proposalProcedureDI.getDataItems().get(3))) + .rewardAccount(HexUtil.encodeHexString(((ByteString) proposalProcedureDI.getDataItems().get(1)).getBytes())) + // TODO: 'govAction' field + .deposit(toBigInteger(proposalProcedureDI.getDataItems().get(0))) + .build(); + + // committee votes + java.util.Map committeeVotes = new HashMap<>(); + var committeeVotesDI = (Map) proposalArray.getDataItems().get(1); + + for (DataItem key : committeeVotesDI.getKeys()) { + var credentialDI = (Array) key; + int credType = toInt(credentialDI.getDataItems().get(0)); + String credHash = HexUtil.encodeHexString(((ByteString) credentialDI.getDataItems().get(1)).getBytes()); + var voteDI = committeeVotesDI.get(credentialDI); + Vote vote = Vote.values()[toInt(voteDI)]; + committeeVotes.put(Credential.builder() + .type(credType == 0 ? StakeCredType.ADDR_KEYHASH : StakeCredType.SCRIPTHASH) + .hash(credHash) + .build(), vote); + } + + // dRep votes + java.util.Map dRepVotes = new HashMap<>(); + var dRepVotesDI = (Map) proposalArray.getDataItems().get(2); + + for (DataItem key : dRepVotesDI.getKeys()) { + var credentialDI = (Array) key; + int credType = toInt(credentialDI.getDataItems().get(0)); + String credHash = HexUtil.encodeHexString(((ByteString)credentialDI.getDataItems().get(1)).getBytes()); + var voteDI = dRepVotesDI.get(credentialDI); + Vote vote = Vote.values()[toInt(voteDI)]; + dRepVotes.put(credType == 0 ? Drep.addrKeyHash(credHash) : Drep.scriptHash(credHash), vote); + } + + // stake pool votes + java.util.Map stakePoolVotes = new HashMap<>(); + var stakePoolVotesDI = (Map) proposalArray.getDataItems().get(3); + for (DataItem key : stakePoolVotesDI.getKeys()) { + String poolHash = HexUtil.encodeHexString(((ByteString)key).getBytes()); + var voteDI = stakePoolVotesDI.get(key); + Vote vote = Vote.values()[toInt(voteDI)]; + stakePoolVotes.put(StakePoolId.builder().poolKeyHash(poolHash).build(), vote); + } + + // expiredAfter + Integer expiredAfter = toInt(proposalArray.getDataItems().get(6)); + // proposedIn + Integer proposedIn = toInt(proposalArray.getDataItems().get(5)); + + return Proposal.builder() + .govActionId(govActionId) + .dRepVotes(dRepVotes) + .stakePoolVotes(stakePoolVotes) + .proposalProcedure(proposalProcedure) + .expiredAfter(expiredAfter) + .proposedIn(proposedIn) + .build(); + } + + public List deserializeGovActionIdListResult(DataItem govActionsDI) { + List govActionIds = new ArrayList<>(); + + Array govActionsArray = (Array) govActionsDI; + for (DataItem item : govActionsArray.getDataItems()) { + if (item == SimpleValue.BREAK) { + continue; + } + govActionIds.add(deserializeGovActionIdResult(item)); + } + + return govActionIds; + } + + public GovActionId deserializeGovActionIdResult(DataItem govActionId) { + Array govActionIdDI = (Array) govActionId; + + if (govActionIdDI.getDataItems().isEmpty()) { + return null; + } + + return GovActionId.builder() + .transactionId(HexUtil.encodeHexString(((ByteString) govActionIdDI.getDataItems().get(0)).getBytes())) + .gov_action_index(toInt(govActionIdDI.getDataItems().get(1))) + .build(); + } + + public Committee deserializeCommitteeResult(List committeeDIList) { + var committeeMapDI = (Map) committeeDIList.get(0); + java.util.Map committeeColdCredentialEpoch = new LinkedHashMap<>(); + + for (DataItem di : committeeMapDI.getKeys()) { + var key = (Array) di; + int credType = toInt(key.getDataItems().get(0)); + String credentialHash = HexUtil.encodeHexString(((ByteString)key.getDataItems().get(1)).getBytes()); + Credential credential = Credential.builder() + .type(credType == 1 ? StakeCredType.SCRIPTHASH: StakeCredType.ADDR_KEYHASH) + .hash(credentialHash) + .build(); + var expiredEpochDI = committeeMapDI.get(key); + committeeColdCredentialEpoch.put(credential, toLong(expiredEpochDI)); + } + + var committeeThresholdDI = (RationalNumber) committeeDIList.get(1); + BigDecimal committeeThreshold = toRationalNumber(committeeThresholdDI); + + return Committee.builder() + .committeeColdCredentialEpoch(committeeColdCredentialEpoch) + .threshold(committeeThreshold) + .build(); + } + + public Constitution deserializeConstitutionResult(DataItem constitutionDI) { + Anchor anchor = AnchorSerializer.INSTANCE.deserializeDI(constitutionDI); + return Constitution.builder().anchor(anchor).build(); + } + + public ProtocolParamUpdate deserializePPResult(List paramsDIList) { + if (paramsDIList.isEmpty()) { + return null; + } + + DataItem itemDI = paramsDIList.get(0); + Integer minFeeA = itemDI != null ? toInt(itemDI) : null; + + itemDI = paramsDIList.get(1); + Integer minFeeB = itemDI != null ? toInt(itemDI) : null; + + itemDI = paramsDIList.get(2); + Integer maxBlockSize = itemDI != null ? toInt(itemDI) : null; + + itemDI = paramsDIList.get(3); + Integer maxTxSize = itemDI != null ? toInt(itemDI) : null; + + itemDI = paramsDIList.get(4); + Integer maxBlockHeaderSize = itemDI != null ? toInt(itemDI) : null; + + itemDI = paramsDIList.get(5); + BigInteger keyDeposit = itemDI != null ? toBigInteger(itemDI) : null; + + itemDI = paramsDIList.get(6); + BigInteger poolDeposit = itemDI != null ? toBigInteger(itemDI) : null; + + itemDI = paramsDIList.get(7); + Integer maxEpoch = itemDI != null ? toInt(itemDI) : null; + + itemDI = paramsDIList.get(8); + Integer nOpt = itemDI != null ? toInt(itemDI) : null; + + itemDI = paramsDIList.get(9); + BigDecimal poolPledgeInfluence = itemDI != null ? toRationalNumber(itemDI) : null; + + itemDI = paramsDIList.get(10); + BigDecimal expansionRate = itemDI != null ? toRationalNumber(itemDI) : null; + + itemDI = paramsDIList.get(11); + BigDecimal treasuryGrowthRate = itemDI != null ? toRationalNumber(itemDI) : null; + + Integer protocolMajorVersion = null; + Integer protocolMinorVersion = null; + itemDI = paramsDIList.get(12); + if (itemDI != null) { + List protocolVersions = ((Array) itemDI).getDataItems(); + protocolMajorVersion = toInt(protocolVersions.get(0)); + protocolMinorVersion = toInt(protocolVersions.get(1)); + } + + itemDI = paramsDIList.get(13); + BigInteger minPoolCost = itemDI != null ? toBigInteger(itemDI) : null; + + itemDI = paramsDIList.get(14); + BigInteger adaPerUtxoBytes = itemDI != null ? toBigInteger(itemDI) : null; + + //CostModels + java.util.Map costModelMap = null; + itemDI = paramsDIList.get(15); + if (itemDI != null) { + costModelMap = new LinkedHashMap<>(); + Map itemDIMap = (Map) itemDI; + for (DataItem key : itemDIMap.getKeys()) { + Integer version = toInt(key); + String costModel = HexUtil.encodeHexString(CborSerializationUtil.serialize(itemDIMap.get(key))); + costModelMap.put(version, costModel); + } + } + + //exUnits prices + BigDecimal priceMem = null; + BigDecimal priceSteps = null; + itemDI = paramsDIList.get(16); + if (itemDI != null) { + List exUnitPriceList = ((Array) itemDI).getDataItems(); + Tuple tuple = getExUnitPrices(exUnitPriceList); + priceMem = tuple._1; + priceSteps = tuple._2; + } + + //max tx exunits + BigInteger maxTxExMem = null; + BigInteger maxTxExSteps = null; + itemDI = paramsDIList.get(17); + if (itemDI != null) { + List exUnits = ((Array) itemDI).getDataItems(); + Tuple tuple = getExUnits(exUnits); + maxTxExMem = tuple._1; + maxTxExSteps = tuple._2; + } + + //max block exunits + BigInteger maxBlockExMem = null; + BigInteger maxBlockExSteps = null; + itemDI = paramsDIList.get(18); + if (itemDI != null) { + List exUnits = ((Array) itemDI).getDataItems(); + Tuple tuple = getExUnits(exUnits); + maxBlockExMem = tuple._1; + maxBlockExSteps = tuple._2; + } + + itemDI = paramsDIList.get(19); + Long maxValueSize = itemDI != null ? toLong(itemDI) : null; + + itemDI = paramsDIList.get(20); + Integer collateralPercent = itemDI != null ? toInt(itemDI) : null; + + Integer maxCollateralInputs = null; + itemDI = paramsDIList.get(21); + maxCollateralInputs = itemDI != null ? toInt(itemDI) : null; + + //Pool Voting Threshold + itemDI = paramsDIList.get(22); + BigDecimal motionNoConfidence = null; + BigDecimal committeeNormal = null; + BigDecimal committeeNoConfidence = null; + BigDecimal hardForkInitiation = null; + BigDecimal ppSecurityGroup = null; + if (itemDI != null) { + List poolVotingThresholdList = ((Array) itemDI).getDataItems(); + if (poolVotingThresholdList.size() != 5) + throw new IllegalStateException("Invalid pool voting threshold list"); + + var pvtMotionNoConfidenceDI = (RationalNumber) poolVotingThresholdList.get(0); + var pvtCommitteeNormalDI = (RationalNumber) poolVotingThresholdList.get(1); + var pvtCommitteeNoConfidenceDI = (RationalNumber) poolVotingThresholdList.get(2); + var pvtHardForkInitiationDI = (RationalNumber) poolVotingThresholdList.get(3); + var pvtPPSecurityGroupDI = (RationalNumber) poolVotingThresholdList.get(4); + + motionNoConfidence = toRationalNumber(pvtMotionNoConfidenceDI); + committeeNormal = toRationalNumber(pvtCommitteeNormalDI); + committeeNoConfidence = toRationalNumber(pvtCommitteeNoConfidenceDI); + hardForkInitiation = toRationalNumber(pvtHardForkInitiationDI); + ppSecurityGroup = toRationalNumber(pvtPPSecurityGroupDI); + } + + //DRep voting thresholds + itemDI = paramsDIList.get(23); + BigDecimal dvtMotionNoConfidence = null; + BigDecimal dvtCommitteeNormal = null; + BigDecimal dvtCommitteeNoConfidence = null; + BigDecimal dvtUpdateToConstitution = null; + BigDecimal dvtHardForkInitiation = null; + BigDecimal dvtPPNetworkGroup = null; + BigDecimal dvtPPEconomicGroup = null; + BigDecimal dvtPPTechnicalGroup = null; + BigDecimal dvtPPGovGroup = null; + BigDecimal dvtTreasuryWithdrawal = null; + + if (itemDI != null) { + List dRepVotingThresholdList = ((Array) itemDI).getDataItems(); + if (dRepVotingThresholdList.size() != 10) + throw new IllegalStateException("Invalid dRep voting threshold list"); + + var dvtMotionNoConfidenceDI = (RationalNumber) dRepVotingThresholdList.get(0); + var dvtCommitteeNormalDI = (RationalNumber) dRepVotingThresholdList.get(1); + var dvtCommitteeNoConfidenceDI = (RationalNumber) dRepVotingThresholdList.get(2); + var dvtUpdateToConstitutionDI = (RationalNumber) dRepVotingThresholdList.get(3); + var dvtHardForkInitiationDI = (RationalNumber) dRepVotingThresholdList.get(4); + var dvtPPNetworkGroupDI = (RationalNumber) dRepVotingThresholdList.get(5); + var dvtPPEconomicGroupDI = (RationalNumber) dRepVotingThresholdList.get(6); + var dvtPPTechnicalGroupDI = (RationalNumber) dRepVotingThresholdList.get(7); + var dvtPPGovGroupDI = (RationalNumber) dRepVotingThresholdList.get(8); + var dvtTreasuryWithdrawalDI = (RationalNumber) dRepVotingThresholdList.get(9); + + dvtMotionNoConfidence = toRationalNumber(dvtMotionNoConfidenceDI); + dvtCommitteeNormal = toRationalNumber(dvtCommitteeNormalDI); + dvtCommitteeNoConfidence = toRationalNumber(dvtCommitteeNoConfidenceDI); + dvtUpdateToConstitution = toRationalNumber(dvtUpdateToConstitutionDI); + dvtHardForkInitiation = toRationalNumber(dvtHardForkInitiationDI); + dvtPPNetworkGroup = toRationalNumber(dvtPPNetworkGroupDI); + dvtPPEconomicGroup = toRationalNumber(dvtPPEconomicGroupDI); + dvtPPTechnicalGroup = toRationalNumber(dvtPPTechnicalGroupDI); + dvtPPGovGroup = toRationalNumber(dvtPPGovGroupDI); + dvtTreasuryWithdrawal = toRationalNumber(dvtTreasuryWithdrawalDI); + } + + itemDI = paramsDIList.get(24); + Integer minCommitteeSize = itemDI != null ? toInt(itemDI) : null; + + itemDI = paramsDIList.get(25); + Integer committeeTermLimit = itemDI != null ? toInt(itemDI) : null; + + itemDI = paramsDIList.get(26); + Integer governanceActionValidityPeriod = itemDI != null ? toInt(itemDI) : null; + + itemDI = paramsDIList.get(27); + BigInteger governanceActionDeposit = itemDI != null ? toBigInteger(itemDI) : null; + + itemDI = paramsDIList.get(28); + BigInteger drepDeposit = itemDI != null ? toBigInteger(itemDI) : null; + + itemDI = paramsDIList.get(29); + Integer drepInactivityPeriod = itemDI != null ? toInt(itemDI) : null; + + BigDecimal minFeeRefScriptCostPerByte = null; //TODO -- Remove if condition once this is available in the node release + if (paramsDIList.size() > 30) { + itemDI = paramsDIList.get(30); + minFeeRefScriptCostPerByte = itemDI != null ? toRationalNumber(itemDI) : null; + } + + return ProtocolParamUpdate.builder() + .minFeeA(minFeeA) + .minFeeB(minFeeB) + .maxBlockSize(maxBlockSize) + .maxTxSize(maxTxSize) + .maxBlockHeaderSize(maxBlockHeaderSize) + .keyDeposit(keyDeposit) + .poolDeposit(poolDeposit) + .maxEpoch(maxEpoch) + .nOpt(nOpt) + .poolPledgeInfluence(poolPledgeInfluence) + .expansionRate(expansionRate) + .treasuryGrowthRate(treasuryGrowthRate) + .protocolMajorVer(protocolMajorVersion) + .protocolMinorVer(protocolMinorVersion) + .minPoolCost(minPoolCost) + .adaPerUtxoByte(adaPerUtxoBytes) + .costModels(costModelMap) + .priceMem(priceMem) + .priceStep(priceSteps) + .maxTxExMem(maxTxExMem) + .maxTxExSteps(maxTxExSteps) + .maxBlockExMem(maxBlockExMem) + .maxBlockExSteps(maxBlockExSteps) + .maxValSize(maxValueSize) + .collateralPercent(collateralPercent) + .maxCollateralInputs(maxCollateralInputs) + .poolVotingThresholds(PoolVotingThresholds.builder() + .pvtMotionNoConfidence(motionNoConfidence) + .pvtCommitteeNormal(committeeNormal) + .pvtCommitteeNoConfidence(committeeNoConfidence) + .pvtHardForkInitiation(hardForkInitiation) + .pvtPPSecurityGroup(ppSecurityGroup) + .build()) + .drepVotingThresholds(DrepVoteThresholds.builder() + .dvtMotionNoConfidence(dvtMotionNoConfidence) + .dvtCommitteeNormal(dvtCommitteeNormal) + .dvtCommitteeNoConfidence(dvtCommitteeNoConfidence) + .dvtUpdateToConstitution(dvtUpdateToConstitution) + .dvtHardForkInitiation(dvtHardForkInitiation) + .dvtPPNetworkGroup(dvtPPNetworkGroup) + .dvtPPEconomicGroup(dvtPPEconomicGroup) + .dvtPPTechnicalGroup(dvtPPTechnicalGroup) + .dvtPPGovGroup(dvtPPGovGroup) + .dvtTreasuryWithdrawal(dvtTreasuryWithdrawal) + .build()) + .committeeMinSize(minCommitteeSize) + .committeeMaxTermLength(committeeTermLimit) + .govActionLifetime(governanceActionValidityPeriod) + .govActionDeposit(governanceActionDeposit) + .drepDeposit(drepDeposit) + .drepActivity(drepInactivityPeriod) + .minFeeRefScriptCostPerByte(minFeeRefScriptCostPerByte) + .build(); + } + + private Tuple getExUnits(List exunits) { + BigInteger mem = toBigInteger(exunits.get(0)); + + BigInteger steps = null; + if (exunits.size() > 1) + steps = toBigInteger(exunits.get(1)); + + return new Tuple<>(mem, steps); + } + + private Tuple getExUnitPrices(List exunits) { + RationalNumber memPriceRN = (RationalNumber) exunits.get(0); + RationalNumber stepPriceRN = (RationalNumber) exunits.get(1); + + BigDecimal memPrice = toRationalNumber(memPriceRN); + BigDecimal stepPrice = toRationalNumber(stepPriceRN); + + return new Tuple<>(memPrice, stepPrice); + } +} diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GetRatifyStateQueryResult.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GetRatifyStateQueryResult.java new file mode 100644 index 00000000..18e95f16 --- /dev/null +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GetRatifyStateQueryResult.java @@ -0,0 +1,14 @@ +package com.bloxbean.cardano.yaci.core.protocol.localstate.queries; + +import com.bloxbean.cardano.yaci.core.protocol.localstate.api.QueryResult; +import com.bloxbean.cardano.yaci.core.protocol.localstate.queries.model.RatifyState; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +@Getter +@AllArgsConstructor +@ToString +public class GetRatifyStateQueryResult implements QueryResult { + private RatifyState ratifyState; +} diff --git a/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GovStateQuery.java b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GovStateQuery.java index c59d87ee..ea2a9ae1 100644 --- a/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GovStateQuery.java +++ b/core/src/main/java/com/bloxbean/cardano/yaci/core/protocol/localstate/queries/GovStateQuery.java @@ -584,6 +584,7 @@ public Proposal deserializeProposalResult(DataItem proposalDI) { return Proposal.builder() .govActionId(govActionId) .dRepVotes(dRepVotes) + .committeeVotes(committeeVotes) .stakePoolVotes(stakePoolVotes) .proposalProcedure(proposalProcedure) .expiredAfter(expiredAfter) diff --git a/gradle.properties b/gradle.properties index 8e4855ed..2be1bfbe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ group = com.bloxbean.cardano artifactId = yaci -version = 0.3.1-SNAPSHOT +version = 0.3.3-SNAPSHOT diff --git a/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/BlockFetcherIT.java b/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/BlockFetcherIT.java index 28a784cf..9ad05a36 100644 --- a/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/BlockFetcherIT.java +++ b/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/BlockFetcherIT.java @@ -282,6 +282,42 @@ public void fetchBlock_datumArrayWith258Tag() throws InterruptedException { assertThat(datums.get(0).getHash()).isEqualTo("0956d8af620f0b1b0b8cc6fb7860f22251a99cfb919e2f17f38d1878e7a992c4"); } + @Test + public void fetchBlock_checkConwayRedeemerData() throws InterruptedException { + VersionTable versionTable = N2NVersionTableConstant.v4AndAbove(protocolMagic); + BlockFetcher blockFetcher = new BlockFetcher(node, nodePort, versionTable); + + CountDownLatch countDownLatch = new CountDownLatch(1); + + List redeemers = new ArrayList<>(); + blockFetcher.start(block -> { + var redeemerList = block.getTransactionWitness() + .stream() + .flatMap(witness -> witness.getRedeemers().stream()) + .collect(Collectors.toList()); + redeemers.addAll(redeemerList); + countDownLatch.countDown(); + }); + + Point from = new Point(72509807, "8ad295a3703e6d301390e645f869e2d944bc8f93760b1d526837ec9aec843175"); + Point to = new Point(72509807, "8ad295a3703e6d301390e645f869e2d944bc8f93760b1d526837ec9aec843175"); + blockFetcher.fetch(from, to); + + countDownLatch.await(10, TimeUnit.SECONDS); + blockFetcher.shutdown(); + + assertThat(redeemers).hasSize(2); + assertThat(redeemers.get(0).getTag()).isEqualTo(RedeemerTag.Spend); + assertThat(redeemers.get(0).getData().getCbor()).isEqualTo("d87980"); + assertThat(redeemers.get(0).getExUnits().getMem()).isEqualTo(1173861); + assertThat(redeemers.get(0).getExUnits().getSteps()).isEqualTo(265506430); + + assertThat(redeemers.get(1).getTag()).isEqualTo(RedeemerTag.Spend); + assertThat(redeemers.get(1).getData().getCbor()).isEqualTo("d8799f4d48656c6c6f2c20576f726c6421d8799fd8799f581cfaef5b76acec064fa5f4855885158fb89f89490f4289d17558d3f188ffd87a80ffff"); + assertThat(redeemers.get(1).getExUnits().getMem()).isEqualTo(44519); + assertThat(redeemers.get(1).getExUnits().getSteps()).isEqualTo(13997977); + } + /** Not able to fetch block 0 @Test public void fetchGenesisBlockByron() throws InterruptedException { diff --git a/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/LocalStateQueryClientIT.java b/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/LocalStateQueryClientIT.java index 5ab90f24..1ca2d892 100644 --- a/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/LocalStateQueryClientIT.java +++ b/helper/src/integrationTest/java/com/bloxbean/cardano/yaci/helper/LocalStateQueryClientIT.java @@ -4,6 +4,7 @@ import com.bloxbean.cardano.client.transaction.spec.governance.DRep; import com.bloxbean.cardano.yaci.core.model.Credential; import com.bloxbean.cardano.yaci.core.model.certs.StakeCredType; +import com.bloxbean.cardano.yaci.core.model.governance.GovActionId; import com.bloxbean.cardano.yaci.core.protocol.chainsync.messages.Point; import com.bloxbean.cardano.yaci.core.protocol.localstate.api.Era; import com.bloxbean.cardano.yaci.core.protocol.localstate.queries.*; @@ -376,4 +377,26 @@ void SPOStakeDistrQuery() { SPOStakeDistributionQueryResult result = mono.block(); assertThat(result.getSpoStakeMap()).hasSize(1); } + + @Test + void getProposal() { + Mono mono = localStateQueryClient.executeQuery(new GetProposalsQuery(List.of( + GovActionId.builder().transactionId("0ecc74fe26532cec1ab9a299f082afc436afc888ca2dc0fc6acda431c52dc60d") + .gov_action_index(0).build() + ))); + + GetProposalQueryResult result = mono.block(); + + assertThat(result.getProposals()).isNotNull(); + } + + @Test + void getRatifyState() { + Mono mono = localStateQueryClient.executeQuery(new GetRatifyStateQuery()); + + GetRatifyStateQueryResult result = mono.block(); + + assertThat(result.getRatifyState()).isNotNull(); + } + }