Skip to content

Commit

Permalink
wip: block creation testing cycle 1, more in comments
Browse files Browse the repository at this point in the history
- Submit offchain TX over API for new testnet (still in progress)
- Smart contract execution & hook in with webassembly VM
- Offchain TX support (wip)
- Add deserialize function for BlsCiruit
- Custom webassembly compiler to handle embedded async
- Changes to WASM VM imports structure
- Introduction of VSC smart contract SDK
- New smart contract deployment script
- MVP block creation & verification (no ingestion pipeline yet/inflow of blocks)
- Smart address indexing wip
  • Loading branch information
vaultec81 committed Jan 2, 2024
1 parent ee6920f commit 93e5480
Show file tree
Hide file tree
Showing 19 changed files with 1,110 additions and 382 deletions.
11 changes: 11 additions & 0 deletions src/modules/api/graphql/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { appContainer } from '../index'
import { GraphQLError } from 'graphql';
import * as IPFS from 'kubo-rpc-client'
import sift from 'sift'
import DAGCbor from 'ipld-dag-cbor'
import { TransactionDbStatus, TransactionDbType } from '../../../types';
import { verifyTx } from '../../../services/new/utils';

export const DebugResolvers = {
peers: async (_, args) => {
Expand Down Expand Up @@ -399,5 +401,14 @@ export const Resolvers = {
tx_id: root.toString()
}
}
},
submitTransactionV1: async (_, args) => {
console.log(args.payload)
const buf = Buffer.from(args.payload, 'base64')

const decodedBuf = DAGCbor.util.deserialize(buf)
console.log(decodedBuf)
console.log(await verifyTx(decodedBuf, appContainer.self.identity))
await appContainer.self.newService.transactionPool.broadcastRawTx(decodedBuf)
}
}
1 change: 1 addition & 0 deletions src/modules/api/graphql/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export const schema = `
findLedgerTXs(byContractId: String, byToFrom: String): FindtransactionResult
submitTransaction(payload: String): TransactionSubmitResult
submitTransactionV1(payload: String): TransactionSubmitResult
localNodeInfo: LocalNodeInfo
witnessNodes: [WitnessNode]
nextWitnessSlot(local: Boolean): JSON
Expand Down
38 changes: 38 additions & 0 deletions src/scripts/new/execute-contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { PrivateKey } from "@hiveio/dhive"
import { HiveClient } from "../../utils"
import { CoreService } from "../../services"

const contractId = 'vs41q9c3ygy36jwdd06qe4wgmhxm8m3dxvapy69ckdgn6tp6esuusvd7tupu7smvypyg'

void (async () => {
const core = new CoreService({
prefix: 'manual tx core',
printMetadata: true,
level: 'debug',
mode: 'lite'
})

await core.start()

const broadcast = await HiveClient.broadcast.json({

required_auths: [],
required_posting_auths: [process.env.HIVE_ACCOUNT],
id: "vsc.tx",
json: JSON.stringify({
net_id: core.config.get('network.id'),
__v: '0.1',
__t: 'native',
data: {
op: 'call_contract',
action: 'testJSON',
contract_id: contractId,
payload: {
testData: "hello-world"
}
}
})
}, PrivateKey.fromString(process.env.HIVE_ACCOUNT_POSTING))
console.log(broadcast)
process.exit()
})()
4 changes: 3 additions & 1 deletion src/services/chainBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,9 @@ export class ChainBridge {
})
}
} catch(ex) {
console.log(ex)
if(!ex.message.includes('Unexpected end of JSON input')) {
console.log(ex)
}
}
}
if (op_id === "custom_json") {
Expand Down
62 changes: 52 additions & 10 deletions src/services/new/chainBridgeV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { Collection, Db } from "mongodb";
import DeepEqual from 'deep-equal'
import PQueue from "p-queue";
import { NewCoreService } from ".";
import { HiveAccountAuthority } from "./types";
import { BlockHeader, HiveAccountAuthority } from "./types";
import networks from "../networks";
import { createMongoDBClient, fastStream, sleep } from "../../utils";
import { BlsCircuit } from './utils/crypto/bls-did';
import BitSet from 'bitset';



Expand All @@ -32,6 +34,7 @@ export class ChainBridgeV2 {
witnessHistoryDb: Collection
consensusDb: Collection
consensusDataDb: Collection
blockHeaders: Collection<BlockHeader>
pinQueue: PQueue;
self: NewCoreService;

Expand All @@ -57,14 +60,16 @@ export class ChainBridgeV2 {
if (op === "account_update") {
transactions.push({
operations: tx.operations,
transaction_id: tx.transaction_id
transaction_id: tx.transaction_id,
index: block.transactions.indexOf(tx)
})
} else if (op === 'custom_json') {
try {
if (opPayload.id.startsWith('vsc.')) {
transactions.push({
operations: tx.operations,
transaction_id: tx.transaction_id
transaction_id: tx.transaction_id,
index: block.transactions.indexOf(tx)
})
}
} catch {
Expand Down Expand Up @@ -321,14 +326,51 @@ export class ChainBridgeV2 {
// }, {
// upsert: true
// })
if(json.block_hash) {
this.pinQueue.add(async() => {
// console.log(json.block_hash)
await this.self.ipfs.pin.add(IPFS.CID.parse(json.block_hash), {
recursive: false

if(opPayload.id === "vsc.propose_block" && json.net_id === this.self.config.get('network.id')) {
//Initial checks passed
const slotHeight = Number(blk.key);
const witnessSet = (await this.getWitnessesAtBlock(slotHeight)).map(e => {
return e.keys.find(key => {
console.log(key)
return key.t === "consensus"
})
await this.self.ipfs.pin.rm(IPFS.CID.parse(json.block_hash))
}).filter(e => !!e).map(e => e.key)
const witnessSchedule = await this.self.witness.roundCheck(slotHeight)

//Check witnessSlot validity prior to validation
const witnessSlot = witnessSchedule.find(e => {
return e.bn === slotHeight && e.account === opPayload.required_auths[0]
})

if(witnessSlot) {
const signedBlock = {
...json.signed_block,
block: IPFS.CID.parse(json.signed_block.block)
}

const circuit = BlsCircuit.deserialize(signedBlock, witnessSet)

let pubKeys = []
for(let pub of circuit.aggPubKeys) {
pubKeys.push(pub)
}

if(circuit.verifyPubkeys(pubKeys)) {

}


this.pinQueue.add(async() => {
// console.log(json.block_hash)
await this.self.ipfs.pin.add(IPFS.CID.parse(json.block_hash), {
recursive: false
})
await this.self.ipfs.pin.rm(IPFS.CID.parse(json.block_hash))
})
}


}
}
} catch (ex) {
Expand Down Expand Up @@ -497,7 +539,7 @@ export class ChainBridgeV2 {
} catch {

}
const startBlock = (await this.streamState.findOne({ id: "last_hb" }) || {} as any).val || networks['testnet/d12e6110-9c8c-4498-88f8-67ddf90d451c'].genesisDay
const startBlock = (await this.streamState.findOne({ id: "last_hb" }) || {} as any).val || networks[this.self.config.get('network.id')].genesisDay
console.log('start block is', startBlock)
this.stream = await fastStream.create({
startBlock
Expand Down
150 changes: 150 additions & 0 deletions src/services/new/contractEngineV2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { Collection } from "mongodb";
import { NewCoreService } from ".";
import { AddrRecord } from "./types";
import { encodePayload } from 'dag-jose-utils'
import { bech32 } from "bech32";
import { VmContainer } from "./vm/utils";


enum ContractErrors {
success = 0,
invalid_action = -1,
runtime_error = -2
}

export class ContractEngineV2 {
self: NewCoreService;
addrsDb: Collection<AddrRecord>;
contractDb: Collection<{
id: string
code: string
name: string
description:string
creator: string
}>;

constructor(self: NewCoreService) {
this.self = self;


this.blockTick = this.blockTick.bind(this)
}

async blockTick([opPayload, tx]) {
console.log('opPayload, tx', opPayload, tx)
for(let index in tx.operations) {
const [opName, op] = tx.operations[index]
const json = JSON.parse(op.json)

console.log('OPPAYLOAD DATA INSERT', op, opName)
if(op.id === "vsc.create_contract") {
const contractIdHash = (await encodePayload({
ref_id: tx.transaction_id,
index
})).cid

const bech32Addr = bech32.encode('vs4', bech32.toWords(contractIdHash.bytes));

console.log('smart contract addr', bech32Addr)
await this.contractDb.findOneAndUpdate({
id: bech32Addr
}, {
$set: {
code: json.code,
name: json.name,
description: json.description,
creator: opPayload.required_auths[0],
state_merkle: (await this.self.ipfs.object.new({ template: 'unixfs-dir' })).toString(),
ref_id: tx.transaction_id
}
}, {
upsert: true
})
}
}
}

async init() {
this.addrsDb = this.self.db.collection('addrs')
this.contractDb = this.self.db.collection('contracts')
this.self.chainBridge.registerTickHandle('contract-engine', this.blockTick, {
type: 'tx'
})
}


async createContractOutput(args: {
txs: any
contract_id: string
}) {
const contractInfo = await this.contractDb.findOne({
id: args.contract_id
})
if(!contractInfo) {
throw new Error('Contract not registered with node or does not exist')
}


if(args.txs.length === 0) {
return null;
}

const vm = new VmContainer({
state_merkle: (contractInfo as any).state_merkle,
cid: contractInfo.code,
contract_id: args.contract_id
})

await vm.init()
await vm.onReady()

const txResults = []

for(let tx of args.txs) {
const result = await vm.call({
action: tx.data.action,
payload: JSON.stringify(tx.data.payload)
})


let ret
let code
let msg
if(result.ret) {
const parsedResult: {
msg?: string
code: number
ret?: string
} = JSON.parse((result as any).ret);
ret = parsedResult.ret,
code = parsedResult.code
msg = parsedResult.msg
}
console.log('parsed result', result)
txResults.push({
ret: ret,
code: code || result.errorType,
logs: (result as any).logs,
//Dont store gas usage if 0
...(result.IOGas > 0 ? {gas: result.IOGas} : {})
})
}
const state_merkle = await vm.finishAndCleanup()
console.log('finishing and cleaning up')

const returnObj = {
input_map: args.txs.map(e => e.id),
state_merkle,
results: txResults
}

console.log('returnObj', returnObj)

return returnObj
}

async start() {

}

}
12 changes: 9 additions & 3 deletions src/services/new/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Ed25519Provider } from "key-did-provider-ed25519";
import { DID } from "dids";
import KeyResolver from 'key-did-resolver'
import winston from 'winston';
import { Db } from 'mongodb';
import { Collection, Db } from 'mongodb';
import { Config } from "../nodeConfig";
import { ChainBridgeV2 } from "./chainBridgeV2";
import { NodeIdentity } from "./nodeIdentity";
Expand All @@ -14,6 +14,8 @@ import { getLogger } from '../../logger';
import { createMongoDBClient } from '../../utils';
import { TransactionPoolV2 } from './transactionPool';
import { P2PService } from './p2pService';
import { AddrRecord } from './types';
import { ContractEngineV2 } from './contractEngineV2';

export class NewCoreService {
config: Config;
Expand All @@ -28,6 +30,8 @@ export class NewCoreService {
transactionPool: TransactionPoolV2;
p2pService: P2PService
identity: DID;
addrsDb: Collection<AddrRecord>;
contractEngine: ContractEngineV2;

constructor() {
this.config = new Config(Config.getConfigDir())
Expand All @@ -42,6 +46,7 @@ export class NewCoreService {
this.witness = new WitnessServiceV2(this)
this.p2pService = new P2PService(this)
this.transactionPool = new TransactionPoolV2(this)
this.contractEngine = new ContractEngineV2(this)
}

async init(oldService) {
Expand All @@ -61,8 +66,9 @@ export class NewCoreService {
await this.p2pService.start()
await this.witness.init();
await this.transactionPool.init()


await this.contractEngine.init()

this.addrsDb = this.db.collection('addrs')
}

async start() {
Expand Down
Loading

0 comments on commit 93e5480

Please sign in to comment.