feat: extract newly deployed contract name and Clarity source from core-node event stream

This commit is contained in:
Matthew Little
2020-03-16 18:16:55 +01:00
parent 4d0fc0584d
commit 4b4f32204d
4 changed files with 115 additions and 36 deletions

View File

@@ -3,6 +3,10 @@ import { SmartBuffer } from 'smart-buffer';
import { isEnum } from './helpers';
export class BufferReader extends SmartBuffer {
static fromBuffer(buffer: Buffer): BufferReader {
return new BufferReader({ buff: buffer });
}
readBigUIntLE(length: number): bigint {
const buffer = Buffer.from(this.readBuffer(length)).reverse();
const hex = buffer.toString('hex');

View File

@@ -9,11 +9,7 @@ export enum CoreNodeEventType {
FtMintEvent = 'ft_mint_event',
}
export interface CoreNodeEventMessage {
type: CoreNodeEventType;
}
export interface SmartContractEvent extends CoreNodeEventMessage {
export interface SmartContractEvent {
type: CoreNodeEventType.ContractEvent;
contract_event: {
contract_identifier: string;
@@ -22,46 +18,56 @@ export interface SmartContractEvent extends CoreNodeEventMessage {
};
}
export interface StxTransferEvent extends CoreNodeEventMessage {
export interface StxTransferEvent {
type: CoreNodeEventType.StxTransferEvent;
stx_transfer_event: any;
}
export interface StxMintEvent extends CoreNodeEventMessage {
export interface StxMintEvent {
type: CoreNodeEventType.StxMintEvent;
stx_mint_event: any;
}
export interface StxBurnEvent extends CoreNodeEventMessage {
export interface StxBurnEvent {
type: CoreNodeEventType.StxBurnEvent;
stx_burn_event: any;
}
export interface NftTransferEvent extends CoreNodeEventMessage {
export interface NftTransferEvent {
type: CoreNodeEventType.NftTransferEvent;
nft_transfer_event: any;
}
export interface NftMintEvent extends CoreNodeEventMessage {
export interface NftMintEvent {
type: CoreNodeEventType.NftMintEvent;
nft_mint_event: any;
}
export interface FtTransferEvent extends CoreNodeEventMessage {
export interface FtTransferEvent {
type: CoreNodeEventType.FtTransferEvent;
ft_transfer_event: any;
}
export interface FtMintEvent extends CoreNodeEventMessage {
export interface FtMintEvent {
type: CoreNodeEventType.FtMintEvent;
ft_mint_event: any;
}
export type CoreNodeEvent =
| SmartContractEvent
| StxTransferEvent
| StxMintEvent
| StxBurnEvent
| FtTransferEvent
| FtMintEvent
| NftTransferEvent
| NftMintEvent;
export interface CoreNodeMessage {
block_hash: string;
index_block_hash: string;
parent_block_hash: string;
parent_microblock: string;
events: any[];
events: CoreNodeEvent[];
transactions: string[];
}

View File

@@ -1,13 +1,55 @@
import * as net from 'net';
import { readMessageFromSocket } from './event-stream/reader';
import { CoreNodeMessage } from './event-stream/core-node-message';
import { Transaction, readTransaction, TransactionPayloadTypeID } from './p2p/tx';
import { BufferReader } from './binary-reader';
import { NotImplementedError } from './errors';
import { getEnumDescription } from './helpers';
const server = net.createServer(clientSocket => {
console.log('client connected');
readMessageFromSocket(clientSocket).catch(error => {
async function handleClientMessage(clientSocket: net.Socket): Promise<void> {
let msg: CoreNodeMessage;
const txs: Transaction[] = [];
try {
msg = await readMessageFromSocket(clientSocket);
} catch (error) {
console.error(`error reading messages from socket: ${error}`);
console.error(error);
clientSocket.destroy();
server.close();
return;
}
try {
for (const tx of msg.transactions) {
const txBuffer = Buffer.from(tx.substring(2), 'hex');
const bufferReader = BufferReader.fromBuffer(txBuffer);
const parsedTx = readTransaction(bufferReader);
txs.push(parsedTx);
console.log(parsedTx);
switch (parsedTx.payload.typeId) {
case TransactionPayloadTypeID.Coinbase: {
break;
}
case TransactionPayloadTypeID.SmartContract: {
console.log(`Smart contract deployed: ${parsedTx.payload.name}: ${parsedTx.payload.codeBody}`);
break;
}
default: {
throw new NotImplementedError(
`extracting data for tx type: ${getEnumDescription(TransactionPayloadTypeID, parsedTx.payload.typeId)}`
);
}
}
}
} catch (error) {
console.error(`error parsing message transactions: ${error}`);
console.error(error);
}
}
const server = net.createServer(clientSocket => {
console.log('client connected');
handleClientMessage(clientSocket).catch(error => {
console.error(`error processing socket connection: ${error}`);
console.error(error);
});
clientSocket.on('end', () => {
console.log('client disconnected');

View File

@@ -72,6 +72,15 @@ interface TransactionAuthSponsored {
sponsorCondition: TransactionSpendingCondition;
}
enum TransactionAnchorMode {
/** must be included in a StacksBlock */
OnChainOnly = 1,
/** must be included in a StacksMicroBlock */
OffChainOnly = 2,
/** either */
Any = 3,
}
enum TransactionPostConditionMode {
Allow = 0x01,
Deny = 0x02,
@@ -171,7 +180,7 @@ type TransactionPostCondition =
| TransactionPostConditionFungible
| TransactionPostConditionNonfungible;
enum TransactionPayloadTypeID {
export enum TransactionPayloadTypeID {
TokenTransfer = 0,
SmartContract = 1,
ContractCall = 2,
@@ -196,9 +205,10 @@ interface TransactionPayloadContractCall {
typeId: TransactionPayloadTypeID.ContractCall;
}
// TODO: incomplete
interface TransactionPayloadSmartContract {
typeId: TransactionPayloadTypeID.SmartContract;
name: string;
codeBody: string;
}
// TODO: incomplete
@@ -217,7 +227,7 @@ export interface Transaction {
version: TransactionVersion; // u8
chainId: number; // u32
auth: TransactionAuthStandard | TransactionAuthSponsored;
anchorMode: TransactionPostConditionMode; // u8
anchorMode: TransactionAnchorMode; // u8
postConditionMode: TransactionPostConditionMode; // u8
postConditions: TransactionPostCondition[];
payload: TransactionPayload;
@@ -253,7 +263,7 @@ export function readTransaction(reader: BufferReader): Transaction {
throw new NotImplementedError(`tx auth type: ${getEnumDescription(TransactionAuthTypeID, authType)}`);
}
const anchorMode = reader.readUInt8Enum(TransactionPostConditionMode, n => {
const anchorMode = reader.readUInt8Enum(TransactionAnchorMode, n => {
throw new StacksMessageParsingError(`unexpected tx post condition anchor mode: ${n}`);
});
@@ -300,19 +310,43 @@ function readTransactionPayload(reader: BufferReader): TransactionPayload {
} else if (txPayloadType === TransactionPayloadTypeID.TokenTransfer) {
const payload: TransactionPayloadTokenTransfer = {
typeId: txPayloadType,
address: {
version: reader.readUInt8(),
bytes: reader.readBuffer(20),
},
address: readStacksAddress(reader),
amount: reader.readBigInt64BE(),
memo: reader.readBuffer(34),
};
return payload;
} else if (txPayloadType === TransactionPayloadTypeID.SmartContract) {
const payload: TransactionPayloadSmartContract = {
typeId: txPayloadType,
name: readContractName(reader),
codeBody: readString(reader),
};
return payload;
} else {
throw new NotImplementedError(`tx payload type: ${getEnumDescription(TransactionPayloadTypeID, txPayloadType)}`);
}
}
function readString(reader: BufferReader): string {
const length = reader.readUInt32BE();
const str = reader.readString(length, 'ascii');
return str;
}
function readContractName(reader: BufferReader): string {
const length = reader.readUInt8();
const name = reader.readString(length, 'ascii');
return name;
}
function readStacksAddress(reader: BufferReader): StacksAddress {
const address: StacksAddress = {
version: reader.readUInt8(),
bytes: reader.readBuffer(20),
};
return address;
}
function readTransactionPostConditions(reader: BufferReader): TransactionPostCondition[] {
const conditionCount = reader.readUInt32BE();
const conditions = new Array<TransactionPostCondition>(conditionCount);
@@ -349,23 +383,16 @@ function readTransactionPostConditionPrincipal(reader: BufferReader): PostCondit
} else if (typeId === PostConditionPrincipalTypeID.Standard) {
const principal: PostConditionPrincipalStandard = {
typeId: typeId,
address: {
version: reader.readUInt8(),
bytes: reader.readBuffer(20),
},
address: readStacksAddress(reader),
};
return principal;
} else if (typeId === PostConditionPrincipalTypeID.Contract) {
const address: StacksAddress = {
version: reader.readUInt8(),
bytes: reader.readBuffer(20),
};
const contractNameLen = reader.readUInt8();
const contractNameBuff = reader.readBuffer(contractNameLen);
const address = readStacksAddress(reader);
const contractName = readContractName(reader);
const principal: PostConditionPrincipalContract = {
typeId: typeId,
address: address,
contractName: contractNameBuff.toString('ascii'),
contractName: contractName,
};
return principal;
} else {