@@ -3,6 +3,7 @@ const fs = require('fs');
33const path = require ( 'path' ) ;
44const istanbul = require ( 'istanbul' ) ;
55const util = require ( 'util' ) ;
6+ const assert = require ( 'assert' ) ;
67
78const Instrumenter = require ( './instrumenter' ) ;
89const Coverage = require ( './coverage' ) ;
@@ -113,15 +114,31 @@ class App {
113114 *
114115 * TODO: generalize provider options setting for non-ganache clients..
115116 */
116- provider ( client ) {
117- if ( ! this . client ) this . client = client ;
117+ async provider ( client ) {
118+ let retry = false ;
119+
120+ if ( ! this . client ) this . client = client ; // Prefer client from options
121+
118122 this . collector = new DataCollector ( this . instrumenter . instrumentationData ) ;
119123
120124 this . providerOptions . gasLimit = this . gasLimitString ;
121125 this . providerOptions . allowUnlimitedContractSize = true ;
122- this . providerOptions . logger = { log : this . collector . step . bind ( this . collector ) } ;
123126
124- this . provider = this . client . provider ( this . providerOptions ) ;
127+ // Try to launch provider and attach to vm step of
128+ // either plugin's ganache or a provider passed via options
129+ try {
130+ this . provider = await this . attachToVM ( ) ;
131+ } catch ( err ) {
132+ retry = true ;
133+ this . ui . report ( 'vm-fail' , [ ] )
134+ }
135+
136+ // Fallback to ganache-core-sc (eq: ganache-core 2.7.0)
137+ if ( retry ) {
138+ this . providerOptions . logger = { log : this . collector . step . bind ( this . collector ) } ;
139+ this . client = require ( 'ganache-core-sc' ) ;
140+ this . provider = this . client . provider ( this . providerOptions ) ;
141+ }
125142
126143 return this . provider ;
127144 }
@@ -176,6 +193,50 @@ class App {
176193 }
177194 // ------------------------------------------ Utils ----------------------------------------------
178195
196+ // ========
197+ // Provider
198+ // ========
199+ async attachToVM ( ) {
200+ const self = this ;
201+ const provider = this . client . provider ( this . providerOptions ) ;
202+
203+ this . assertHasBlockchain ( provider ) ;
204+
205+ await this . vmIsResolved ( provider ) ;
206+
207+ const blockchain = provider . engine . manager . state . blockchain ;
208+ const createVM = blockchain . createVMFromStateTrie ;
209+
210+ // Attach to VM which ganache has already instantiated
211+ // and which it uses to execute eth_send
212+ blockchain . vm . on ( 'step' , self . collector . step . bind ( self . collector ) ) ;
213+
214+ // Attach/hijack createVM method which ganache uses to run eth_calls
215+ blockchain . createVMFromStateTrie = function ( state , activatePrecompiles ) {
216+ const vm = createVM . apply ( blockchain , arguments ) ;
217+ vm . on ( 'step' , self . collector . step . bind ( self . collector ) ) ;
218+ return vm ;
219+ }
220+
221+ return provider ;
222+ }
223+
224+ assertHasBlockchain ( provider ) {
225+ assert ( provider . engine . manager . state . blockchain !== undefined ) ;
226+ assert ( provider . engine . manager . state . blockchain . createVMFromStateTrie !== undefined ) ;
227+ }
228+
229+ async vmIsResolved ( provider ) {
230+ return new Promise ( resolve => {
231+ const interval = setInterval ( ( ) => {
232+ if ( provider . engine . manager . state . blockchain . vm !== undefined ) {
233+ clearInterval ( interval ) ;
234+ resolve ( ) ;
235+ }
236+ } ) ;
237+ } )
238+ }
239+
179240 // ========
180241 // File I/O
181242 // ========
0 commit comments