Skip to content

Instantly share code, notes, and snippets.

@rumkin
Last active January 29, 2020 06:36
Show Gist options
  • Select an option

  • Save rumkin/de8f0d51adf711baa1ddfe22be88cb73 to your computer and use it in GitHub Desktop.

Select an option

Save rumkin/de8f0d51adf711baa1ddfe22be88cb73 to your computer and use it in GitHub Desktop.
ES and SQL based smart contract engine

ES and SQL as a smart contract enginge

This is a simple example of a smart contract written in ECMAScript which uses SQL database for storing the state.

class Engine {
  constructor(db, contracts) {
    this.db = db
    this.contracts = contracts
  }
  
  async executeBlock(block) {
    const {db} = this
    
    const t = await db.transaction()
    try { 
      await this.verifyBlock(block, t)
    
      for (const tx of block.txs) {
        try {
          await this.executeTx(tx, t)
        }
        catch (err) {
          if (err instanceof Revert) {
            await this.txFailed(tx, error)
          }
          else {
            throw err
          }
        }
      }
      await this.writeBlock(block, t)
      await t.commit()
    }
    catch (err) {
      await t.rollback()
      throw err
    }
  }
  
  async verifyBlock(block, db) {
    throw new Error('Not implemented yet')
  }
  
  executeTx(tx, db) {
    return this.contracts[tx.target](tx, t)
  }
}

class PosEngine extends Engine {
  async verifyBlock(block, db) {
    const verifiers = await this.db.accounts.select(['address']).sort({value: -1}).limit(10)
    .then((result) => result.map(({address}) => address))
    
    for (const addr of block.voters) {
      if (verifiers.includes(addr) === false) {
        throw new Error(`invalid_voter:${addr}`);
      }  
    }
  }
}

const moneyDb = new PosEngine(db, {
   async accounts(tx, db) {
    const {sender, type, payload} = tx
    
    switch (type) {
      case 'TRANSFER': {
        const {value} = await db.accounts.select().where({address: sender}).findOne()
        if (value < tx.value) {
          throw new Revert('insufficient_funds')
        }
        
        await db.accounts.select({
          address: sender,
        })
        .update({
          $decrease: {value: tx.value},
        })
        
        await db.accounts.select({
          address: payload.receiver,
        })
        .update({
          $increase: {value: tx.value},
        })
      }
      default: {
        throw new Error(`Unknown transaction type: ${type}`)
      }
    }
  }
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment