Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const dotenvPath = path.resolve(__dirname, `.env.${mode}`);
dotenv.config({ path: dotenvPath });

const endpoints: string[] = process.env.ENDPOINT?.split(",") as string[];
console.log(`Endpoints: ${endpoints}`);
console.log(`Parsed Endpoints: ${endpoints}`);

// Can expand the Datasource processor types via the generic param
const project: CosmosProject = {
Expand Down Expand Up @@ -424,6 +424,7 @@ const project: CosmosProject = {
startBlock: 1,
// migration at 25507 on alpha
// msg grants at 23196 on alpha
// mutlsig send tx at 39712 on beta
kind: CosmosDatasourceKind.Runtime,
mapping: {
file: "./dist/index.js",
Expand Down
20 changes: 19 additions & 1 deletion schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,19 @@ type ValidatorCommissionParams @jsonField {
maxChangeRate: String!
}

type Multisig @jsonField {
from: String!
all: [String]!
signed: [String]!
# Using indices, threshold, extraBitsStored, pubkeysBase64, bitarrayElems, extraBitsStored
# allows to rebuild the fields: from, all and signed
indices: [Int]!
threshold: Int!
extraBitsStored: Int!
multisigPubKey: String!
bitarrayElems: String!
}

### ENTITIES

# Represent the balance of an account at the genesis state (usually genesis file)
Expand Down Expand Up @@ -328,8 +341,13 @@ type Transaction @entity {
idx: Int!
codespace: String
timeoutHeight: BigInt @index
# NB: only the first signer!
# Mode = Single -> First signer
# Mode = Multi -> Address from multisig public key
signerAddress: String @index
# indicates if the transaction is signed using /cosmos.crypto.multisig.LegacyAminoPubKey
isMultisig: Boolean!
# could be null is isMulti = false
multisig: Multisig
messages: [Message] @derivedFrom(field: "transaction")
events: [Event]@derivedFrom(field: "transaction")
}
Expand Down
45 changes: 38 additions & 7 deletions src/mappings/pocket/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import {
CosmosMessage,
} from "@subql/types-cosmos";
import type { MsgCreateValidator } from "cosmjs-types/cosmos/staking/v1beta1/tx";
import { isNil } from "lodash";
import {
isEmpty,
isNil,
} from "lodash";
import { parseCoins } from "../../cosmjs/utils";
import {
StakeStatus,
Expand All @@ -13,6 +16,7 @@ import {
import { MsgCreateValidator as MsgCreateValidatorEntity } from "../../types/models/MsgCreateValidator";
import { ValidatorCommissionProps } from "../../types/models/ValidatorCommission";
import { ValidatorRewardProps } from "../../types/models/ValidatorReward";
import { SignerInfo } from "../../types/proto-interfaces/cosmos/tx/v1beta1/tx";
import { enforceAccountsExists } from "../bank";
import {
PREFIX,
Expand All @@ -25,6 +29,11 @@ import {
messageId,
} from "../utils/ids";
import { stringify } from "../utils/json";
import {
extractThresholdAndPubkeysFromMultisig,
getMultiSignPubKeyAddress,
isMulti,
} from "../utils/multisig";
import {
Ed25519,
pubKeyToAddress,
Expand All @@ -36,19 +45,41 @@ async function _handleValidatorMsgCreate(msg: CosmosMessage<MsgCreateValidator>)
const msgId = messageId(msg);
const blockId = getBlockId(msg.block);
const createValMsg = msg.msg.decodedMsg;
const signer = msg.tx.decodedTx.authInfo.signerInfos[0];

if (isNil(signer) || isNil(signer.publicKey)) {
throw new Error("Signer is nil");
if (isEmpty(msg.tx.decodedTx.authInfo.signerInfos) || isNil(msg.tx.decodedTx.authInfo.signerInfos[0]?.publicKey)) {
throw new Error(`[handleValidatorMsgCreate] (block ${msg.block.block.header.height}): hash=${msg.tx.hash} missing signerInfos public key`);
}

const signerInfo = (msg.tx.decodedTx.authInfo.signerInfos as SignerInfo[])[0];

if (!signerInfo.publicKey) {
throw new Error(`[handleValidatorMsgCreate] (block ${msg.tx.block.block.header.height}): hash=${msg.tx.hash} missing signerInfos public key`);
}

const signerType = signerInfo.publicKey.typeUrl;
let signerAddress, poktSignerAddress;

if (isMulti(signerInfo)) {
// TODO: is this doable?
// probably yes, but we should attempt to reproduce this and see if this
// code satisfied this well enough
const { pubkeysBase64, threshold } = extractThresholdAndPubkeysFromMultisig(signerInfo.publicKey.value);
const { from: validatorFrom } = getMultiSignPubKeyAddress(pubkeysBase64, threshold, VALIDATOR_PREFIX);
signerAddress = validatorFrom;
const { from: poktValidatorFrom } = getMultiSignPubKeyAddress(pubkeysBase64, threshold, PREFIX);
poktSignerAddress = poktValidatorFrom;
} else if (signerType === Secp256k1) {
signerAddress = pubKeyToAddress(Secp256k1, signerInfo.publicKey.value, VALIDATOR_PREFIX);
poktSignerAddress = pubKeyToAddress(Secp256k1, signerInfo.publicKey.value, PREFIX);
} else {
signerAddress = `Unsupported Signer: ${signerType}`;
poktSignerAddress = `Unsupported Signer: ${signerType}`;
}

if (isNil(createValMsg.pubkey)) {
throw new Error("Pubkey is nil");
}

const signerAddress = pubKeyToAddress(Secp256k1, signer.publicKey.value, VALIDATOR_PREFIX);
const poktSignerAddress = pubKeyToAddress(Secp256k1, signer.publicKey.value, PREFIX);

const msgCreateValidator = MsgCreateValidatorEntity.create({
id: msgId,
pubkey: {
Expand Down
5 changes: 5 additions & 0 deletions src/mappings/primitives/genesis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ async function _handleGenesisServices(genesis: Genesis, block: CosmosBlock): Pro
memo: "",
log: "",
status: TxStatus.Success,
isMultisig: false,
code: 0,
});

Expand Down Expand Up @@ -447,6 +448,7 @@ async function _handleGenesisSuppliers(genesis: Genesis, block: CosmosBlock): Pr
memo: "",
log: "",
timeoutHeight: BigInt(0),
isMultisig: false,
});

supplierMsgStakes.push({
Expand Down Expand Up @@ -574,6 +576,7 @@ async function _handleGenesisApplications(genesis: Genesis, block: CosmosBlock):
memo: "",
log: "",
status: TxStatus.Success,
isMultisig: false,
code: 0,
});

Expand Down Expand Up @@ -687,6 +690,7 @@ async function _handleGenesisGateways(genesis: Genesis, block: CosmosBlock): Pro
memo: "",
log: "",
status: TxStatus.Success,
isMultisig: false,
code: 0,
});

Expand Down Expand Up @@ -870,6 +874,7 @@ async function _handleGenesisGenTxs(genesis: Genesis, block: CosmosBlock): Promi
memo: "",
log: "",
status: TxStatus.Success,
isMultisig: false,
code: 0,
});
}
Expand Down
63 changes: 56 additions & 7 deletions src/mappings/primitives/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,79 @@ import {
isEmpty,
isNil,
} from "lodash";
import { Multisig } from "../../types";
import { TransactionProps } from "../../types/models/Transaction";
import { SignerInfo } from "../../types/proto-interfaces/cosmos/tx/v1beta1/tx";
import {
PREFIX,
VALIDATOR_PREFIX,
} from "../constants";
import { optimizedBulkCreate } from "../utils/db";
import { getBlockId } from "../utils/ids";
import {
getMultisigInfo,
isMulti,
} from "../utils/multisig";
import {
getTxStatus,
isMsgValidatorRelated,
} from "../utils/primitives";
import { pubKeyToAddress } from "../utils/pub_key";
import {
pubKeyToAddress,
Secp256k1,
} from "../utils/pub_key";

function _handleTransaction(tx: CosmosTransaction): TransactionProps {
let signerAddress;
if (isEmpty(tx.decodedTx.authInfo.signerInfos) || isNil(tx.decodedTx.authInfo.signerInfos[0]?.publicKey)) {

if (isEmpty(tx.decodedTx.authInfo.signerInfos) || isNil(tx.decodedTx.authInfo.signerInfos[0].publicKey)) {
throw new Error(`[handleTransaction] (block ${tx.block.block.header.height}): hash=${tx.hash} missing signerInfos public key`);
} else {
const prefix = isMsgValidatorRelated(tx.decodedTx.body.messages[0].typeUrl) ? VALIDATOR_PREFIX : PREFIX;
// if the first message is a MsgCreateValidator, we assume the signer is the account related to it,
// that is hashed with a different prefix.
}

const prefix = isMsgValidatorRelated(tx.decodedTx.body.messages[0].typeUrl) ? VALIDATOR_PREFIX : PREFIX;

const signerInfo = (tx.decodedTx.authInfo.signerInfos as SignerInfo[])[0];

if (!signerInfo.publicKey) {
throw new Error(`[handleTransaction] (block ${tx.block.block.header.height}): hash=${tx.hash} missing signerInfos public key`);
}

const signerType = signerInfo.publicKey.typeUrl;
const isMultisig = isMulti(signerInfo);
let multisigObject: Multisig | undefined;

if (isMultisig) {
const {
allSignerAddresses,
bitarrayElems,
extraBitsStored,
fromAddress,
multisigPubKey,
signedSignerAddresses,
signerIndices,
threshold,
} = getMultisigInfo(signerInfo);

// TODO: We should probably "create" this account otherwise maybe will not exists?
signerAddress = fromAddress;
multisigObject = {
from: fromAddress,
all: allSignerAddresses,
signed: signedSignerAddresses,
indices: signerIndices,
threshold,
multisigPubKey,
bitarrayElems,
extraBitsStored,
};
} else if (signerType === Secp256k1) {
signerAddress = pubKeyToAddress(
tx.decodedTx.authInfo.signerInfos[0]?.publicKey.typeUrl,
signerType,
tx.decodedTx.authInfo.signerInfos[0]?.publicKey.value,
prefix,
);
} else {
signerAddress = `Unsupported Signer: ${signerType}`;
}

const feeAmount = !isNil(tx.decodedTx.authInfo.fee) ? tx.decodedTx.authInfo.fee.amount : [];
Expand All @@ -45,6 +92,8 @@ function _handleTransaction(tx: CosmosTransaction): TransactionProps {
log: tx.tx.log || "",
status: getTxStatus(tx),
signerAddress,
isMultisig,
multisig: isMultisig ? multisigObject : undefined,
code: tx.tx.code,
codespace: tx.tx.codespace,
};
Expand Down
Loading