Skip to content
This repository was archived by the owner on May 13, 2022. It is now read-only.

Commit b6820f7

Browse files
author
Silas Davis
committed
Fix collisions in ABI output, submit contract metadata on deploy, remove
GRPC dependencies from codegen interface.gd.ts - Switched to a hierarchical bin folder to only require contract names to be unique within their own directory - Send contract ABIs to Burrow from codegen deploy function - Remove dependency on GRPC types to improve ergonomics and not couple GRPC codegen to the provider interface hopefully to give better compatibility between versions - Use duck-typed interface for CancelStreamSignal so that cancel signal is not path dependent (for compatibiltiy between multiple Burrow versions where possible in same project) Signed-off-by: Silas Davis <[email protected]>
1 parent 89c3124 commit b6820f7

31 files changed

+950
-873
lines changed

execution/evm/abi/abi.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ func LoadPath(abiFileOrDirs ...string) (*Spec, error) {
3030
specs := make([]*Spec, 0)
3131

3232
for _, dir := range abiFileOrDirs {
33-
err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
33+
err := filepath.WalkDir(dir, func(path string, dir os.DirEntry, err error) error {
3434
if err != nil {
3535
return fmt.Errorf("error returned while walking abiDir '%s': %v", dir, err)
3636
}
3737
ext := filepath.Ext(path)
38-
if fi.IsDir() || !(ext == ".bin" || ext == ".abi" || ext == ".json") {
38+
if dir.IsDir() || !(ext == ".bin" || ext == ".abi" || ext == ".json") {
3939
return nil
4040
}
4141
abiSpc, err := ReadSpecFile(path)

js/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "0.0.0",
2+
"version": "0.33.1-dev.e4b5f85b9",
33
"name": "@hyperledger/burrow",
44
"description": "TypeScript library that calls a Hyperledger Burrow server over GRPC.",
55
"main": "./dist/index.js",
@@ -21,7 +21,7 @@
2121
},
2222
"dependencies": {
2323
"@grpc/grpc-js": "^1.3.0",
24-
"ethers": "^5.1.4",
24+
"ethers": "^5.2.0",
2525
"google-protobuf": "^3.15.8",
2626
"sha3": "^2.1.4",
2727
"solc_v5": "npm:solc@^0.5.17",

js/src/client.ts

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import * as grpc from '@grpc/grpc-js';
22
import { Interface } from 'ethers/lib/utils';
3-
import { Event, TxExecution } from '../proto/exec_pb';
4-
import { CallTx, ContractMeta } from '../proto/payload_pb';
3+
import { TxExecution } from '../proto/exec_pb';
4+
import { CallTx } from '../proto/payload_pb';
55
import { ExecutionEventsClient, IExecutionEventsClient } from '../proto/rpcevents_grpc_pb';
66
import { IQueryClient, QueryClient } from '../proto/rpcquery_grpc_pb';
77
import { GetMetadataParam, StatusParam } from '../proto/rpcquery_pb';
88
import { ITransactClient, TransactClient } from '../proto/rpctransact_grpc_pb';
99
import { ResultStatus } from '../proto/rpc_pb';
1010
import { ContractCodec, getContractCodec } from './codec';
1111
import { Address } from './contracts/abi';
12-
import { makeCallTx } from './contracts/call';
12+
import { getContractMeta, makeCallTx } from './contracts/call';
1313
import { CallOptions, Contract, ContractInstance } from './contracts/contract';
1414
import { toBuffer } from './convert';
1515
import { getException } from './error';
16-
import { Bounds, EventCallback, EventStream, getBlockRange, queryFor, stream } from './events';
16+
import { Bounds, Event, EventCallback, EventStream, getBlockRange, queryFor, stream } from './events';
1717
import { Namereg } from './namereg';
1818
import { Provider } from './solts/interface.gd';
1919

@@ -125,22 +125,33 @@ export class Client implements Provider {
125125
// Methods below implement the generated codegen provider
126126
// TODO: should probably generate canonical version of Provider interface somewhere outside of files
127127

128-
async deploy(msg: CallTx): Promise<Address> {
129-
const txe = await this.callTxSync(msg);
128+
async deploy(
129+
data: string | Uint8Array,
130+
contractMeta: { abi: string; codeHash: Uint8Array }[] = [],
131+
): Promise<Address> {
132+
const tx = makeCallTx(
133+
toBuffer(data),
134+
this.account,
135+
undefined,
136+
contractMeta.map(({ abi, codeHash }) => getContractMeta(abi, codeHash)),
137+
);
138+
const txe = await this.callTxSync(tx);
130139
const contractAddress = txe.getReceipt()?.getContractaddress_asU8();
131140
if (!contractAddress) {
132141
throw new Error(`deploy appears to have succeeded but contract address is missing from result: ${txe}`);
133142
}
134143
return Buffer.from(contractAddress).toString('hex').toUpperCase();
135144
}
136145

137-
async call(msg: CallTx): Promise<Uint8Array | undefined> {
138-
const txe = await this.callTxSync(msg);
146+
async call(data: string | Uint8Array, address: string): Promise<Uint8Array | undefined> {
147+
const tx = makeCallTx(toBuffer(data), this.account, address);
148+
const txe = await this.callTxSync(tx);
139149
return txe.getResult()?.getReturn_asU8();
140150
}
141151

142-
async callSim(msg: CallTx): Promise<Uint8Array | undefined> {
143-
const txe = await this.callTxSim(msg);
152+
async callSim(data: string | Uint8Array, address: string): Promise<Uint8Array | undefined> {
153+
const tx = makeCallTx(toBuffer(data), this.account, address);
154+
const txe = await this.callTxSim(tx);
144155
return txe.getResult()?.getReturn_asU8();
145156
}
146157

@@ -154,10 +165,6 @@ export class Client implements Provider {
154165
return stream(this.executionEvents, getBlockRange(start, end), queryFor({ address, signatures }), callback);
155166
}
156167

157-
payload(data: string | Uint8Array, address?: string, contractMeta: ContractMeta[] = []): CallTx {
158-
return makeCallTx(typeof data === 'string' ? toBuffer(data) : data, this.account, address, contractMeta);
159-
}
160-
161168
contractCodec(contractABI: string): ContractCodec {
162169
const iface = new Interface(contractABI);
163170
return getContractCodec(iface);

js/src/contracts/call.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import * as grpc from '@grpc/grpc-js';
22
import { Metadata } from '@grpc/grpc-js';
33
import { callErrorFromStatus } from '@grpc/grpc-js/build/src/call';
44
import { AbiCoder, FunctionFragment, Interface, Result as DecodedResult } from 'ethers/lib/utils';
5+
import { Keccak } from 'sha3';
56
import { SolidityFunction } from 'solc';
67
import { Result } from '../../proto/exec_pb';
78
import { CallTx, ContractMeta, TxInput } from '../../proto/payload_pb';
89
import { Envelope } from '../../proto/txs_pb';
910
import { Pipe } from '../client';
1011
import { postDecodeResult, preEncodeResult, toBuffer } from '../convert';
11-
import { Address } from './abi';
12+
import { ABI, Address } from './abi';
1213
import { CallOptions } from './contract';
1314

1415
export const DEFAULT_GAS = 1111111111;
@@ -48,6 +49,22 @@ export function makeCallTx(
4849
return payload;
4950
}
5051

52+
export function getContractMetaFromBytecode(abi: ABI, deployedBytecode: string): ContractMeta {
53+
const hasher = new Keccak(256);
54+
const codeHash = hasher.update(deployedBytecode, 'hex').digest();
55+
return getContractMeta(abi, codeHash);
56+
}
57+
58+
export function getContractMeta(abi: ABI | string, codeHash: Uint8Array): ContractMeta {
59+
const meta = new ContractMeta();
60+
if (typeof abi !== 'string') {
61+
abi = JSON.stringify({ Abi: abi });
62+
}
63+
meta.setMeta(abi);
64+
meta.setCodehash(codeHash);
65+
return meta;
66+
}
67+
5168
export type TransactionResult = {
5269
contractAddress: string;
5370
height: number;

js/src/contracts/compile.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
import solc from 'solc_v5';
2-
import { CompiledContract, Contract } from './contract';
2+
import { ABI } from './abi';
3+
import { Contract } from './contract';
4+
5+
export type CompiledContract = {
6+
abi: ABI;
7+
// Required to deploy a contract
8+
bytecode?: string;
9+
// Required to submit an ABI when deploying a contract
10+
deployedBytecode?: string;
11+
};
312

413
// Compile solidity source code
514
export function compile<T = any>(
@@ -29,7 +38,7 @@ export function compile<T = any>(
2938
);
3039
}
3140

32-
function getCompiledCode(contract: solc.Contract): CompiledContract {
41+
function getCompiledCode(contract: solc.Contract): Required<CompiledContract> {
3342
return {
3443
abi: contract.abi,
3544
bytecode: contract.evm.bytecode.object,

js/src/contracts/contract.ts

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { EventFragment, Fragment, FunctionFragment, Interface, LogDescription } from 'ethers/lib/utils';
2-
import { Keccak } from 'sha3';
32
import { CallTx, ContractMeta } from '../../proto/payload_pb';
43
import { Client } from '../client';
54
import { preEncodeResult, toBuffer } from '../convert';
6-
import { EventStream } from '../events';
7-
import { ABI, Address } from './abi';
8-
import { call, callFunction, CallResult, DecodeResult, makeCallTx } from './call';
5+
import { EventStream } from "../events";
6+
import { Address } from './abi';
7+
import { call, callFunction, CallResult, DecodeResult, getContractMetaFromBytecode, makeCallTx } from './call';
8+
import { CompiledContract } from './compile';
99
import { EventCallback, listen } from './event';
1010

1111
export const meta: unique symbol = Symbol('meta');
@@ -39,14 +39,6 @@ export function getAddress(instance: ContractInstance): Address {
3939
return instance[meta].address;
4040
}
4141

42-
export type CompiledContract = {
43-
abi: ABI;
44-
// Required to deploy a contract
45-
bytecode?: string;
46-
// Required to submit an ABI when deploying a contract
47-
deployedBytecode?: string;
48-
};
49-
5042
export class Contract<T extends ContractInstance | any = any> {
5143
private readonly iface: Interface;
5244

@@ -74,7 +66,9 @@ export class Contract<T extends ContractInstance | any = any> {
7466
}
7567

7668
meta(): ContractMeta[] {
77-
return [this.code, ...this.childCode].map(contractMeta).filter((m): m is ContractMeta => Boolean(m));
69+
return [this.code, ...this.childCode]
70+
.map(({ abi, deployedBytecode }) => abi && deployedBytecode && getContractMetaFromBytecode(abi, deployedBytecode))
71+
.filter((m): m is ContractMeta => Boolean(m));
7872
}
7973

8074
async deploy(burrow: Client, ...args: unknown[]): Promise<T> {
@@ -147,19 +141,6 @@ function contractEvent(iface: Interface, frag: EventFragment, burrow: Client, co
147141
return func;
148142
}
149143

150-
function contractMeta({ abi, deployedBytecode }: CompiledContract): ContractMeta | void {
151-
// We can only calculate the codeHash if we have the deployedCode
152-
if (abi.length == 0 || !deployedBytecode) {
153-
return undefined;
154-
}
155-
const meta = new ContractMeta();
156-
const hasher = new Keccak(256);
157-
const codeHash = hasher.update(deployedBytecode, 'hex').digest();
158-
meta.setMeta(JSON.stringify({ Abi: abi }));
159-
meta.setCodehash(codeHash);
160-
return meta;
161-
}
162-
163144
function attachFunction(
164145
iface: Interface,
165146
frag: Fragment,

js/src/contracts/event.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ export function listen(
2424
if (err) {
2525
return isEndOfStream(err) ? undefined : callback(err);
2626
}
27-
const log = event?.getLog();
27+
const log = event?.log;
2828
if (!log) {
2929
return callback(new Error(`no LogEvent or Error provided`));
3030
}
3131
try {
3232
const result = iface.parseLog({
33-
topics: log.getTopicsList_asU8().map((topic) => prefixedHexString(topic)),
34-
data: prefixedHexString(log.getData_asU8()),
33+
topics: log.topics.map((topic) => prefixedHexString(topic)),
34+
data: prefixedHexString(log.data),
3535
});
3636
return callback(undefined, {
3737
...result,

0 commit comments

Comments
 (0)