Skip to content

Commit

Permalink
Make bytes32 operations more programmer friendly (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
sz-piotr authored Feb 8, 2020
1 parent 145808a commit f54214b
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 60 deletions.
46 changes: 18 additions & 28 deletions packages/evm/src/Bytes32.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,6 @@ export class Bytes32 {
return this.value.fromTwos(256)
}

equals (other: Bytes32) {
return this.value.eq(other.value)
}

/**
* Returns the result of adding the argument to this machine word
*/
Expand Down Expand Up @@ -175,57 +171,51 @@ export class Bytes32 {
}

/**
* Returns ONE if this machine word is lesser than the argument. Otherwise
* returns ZERO
* Returns `true` if this machine word is lesser than the argument. Otherwise
* returns `false`
*/
lt (other: Bytes32) {
const result = this.value.lt(other.value)
return Bytes32.fromBoolean(result)
return this.value.lt(other.value)
}

/**
* Returns ONE if this machine word is greater than the argument. Otherwise
* returns ZERO
* Returns `true` if this machine word is greater than the argument. Otherwise
* returns `false`
*/
gt (other: Bytes32) {
const result = this.value.gt(other.value)
return Bytes32.fromBoolean(result)
return this.value.gt(other.value)
}

/**
* Returns ONE if this machine word is lesser than the argument. Otherwise
* returns ZERO. Treats the contents as two's complement signed integers
* Returns `true` if this machine word is lesser than the argument. Otherwise
* returns `false`. Treats the contents as two's complement signed integers
*/
slt (other: Bytes32) {
const result = this.signed.lt(other.signed)
return Bytes32.fromBoolean(result)
return this.signed.lt(other.signed)
}

/**
* Returns ONE if this machine word is greater than the argument. Otherwise
* returns ZERO. Treats the contents as two's complement signed integers
* Returns `true` if this machine word is greater than the argument. Otherwise
* returns `false`. Treats the contents as two's complement signed integers
*/
sgt (other: Bytes32) {
const result = this.signed.gt(other.signed)
return Bytes32.fromBoolean(result)
return this.signed.gt(other.signed)
}

/**
* Returns ONE if this machine word is equal to the argument. Otherwise
* returns ZERO
* Returns `true` if this machine word is equal to the argument. Otherwise
* returns `false`
*/
eq (other: Bytes32) {
const result = this.value.eq(other.value)
return Bytes32.fromBoolean(result)
return this.value.eq(other.value)
}

/**
* Returns ONE if this machine word is equal to ZERO. Otherwise
* returns ZERO
* Returns `true` if this machine word is equal to ZERO. Otherwise
* returns `false`
*/
iszero () {
const result = this.value.isZero()
return Bytes32.fromBoolean(result)
return this.value.isZero()
}

/**
Expand Down
13 changes: 7 additions & 6 deletions packages/evm/src/opcodes/bytes32.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { makeUnaryOp, makeBinaryOp, makeTernaryOp } from './helpers'
import { GasCost } from './gasCosts'
import { Bytes32 } from '../Bytes32'

// Arithmetic

Expand Down Expand Up @@ -62,32 +63,32 @@ export const opSIGNEXTEND = makeBinaryOp(

export const opLT = makeBinaryOp(
GasCost.VERYLOW,
(a, b) => a.lt(b),
(a, b) => Bytes32.fromBoolean(a.lt(b)),
)

export const opGT = makeBinaryOp(
GasCost.VERYLOW,
(a, b) => a.gt(b),
(a, b) => Bytes32.fromBoolean(a.gt(b)),
)

export const opSLT = makeBinaryOp(
GasCost.VERYLOW,
(a, b) => a.slt(b),
(a, b) => Bytes32.fromBoolean(a.slt(b)),
)

export const opSGT = makeBinaryOp(
GasCost.VERYLOW,
(a, b) => a.sgt(b),
(a, b) => Bytes32.fromBoolean(a.sgt(b)),
)

export const opEQ = makeBinaryOp(
GasCost.VERYLOW,
(a, b) => a.eq(b),
(a, b) => Bytes32.fromBoolean(a.eq(b)),
)

export const opISZERO = makeUnaryOp(
GasCost.VERYLOW,
(a) => a.iszero(),
(a) => Bytes32.fromBoolean(a.iszero()),
)

// Bitwise Logic
Expand Down
3 changes: 1 addition & 2 deletions packages/evm/src/opcodes/control.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ExecutionContext } from '../ExecutionContext'
import { GasCost } from './gasCosts'
import { InvalidJumpDestination } from '../errors'
import { Bytes32 } from '../Bytes32'

export function opSTOP (ctx: ExecutionContext) {
ctx.useGas(GasCost.ZERO)
Expand Down Expand Up @@ -49,7 +48,7 @@ export function opJUMPI (ctx: ExecutionContext) {
const location = destination.toUnsignedNumber()
const condition = ctx.stack.pop()

if (!condition.equals(Bytes32.ZERO)) {
if (!condition.iszero()) {
if (ctx.code[location] === opJUMPDEST) {
ctx.programCounter = location
} else {
Expand Down
5 changes: 2 additions & 3 deletions packages/evm/src/opcodes/storage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Bytes32 } from '../Bytes32'
import { ExecutionContext } from '../ExecutionContext'
import { GasCost, GasRefund } from './gasCosts'

Expand All @@ -7,9 +6,9 @@ export function opSSTORE (ctx: ExecutionContext) {
const value = ctx.stack.pop()

const stored = ctx.state.getStorage(ctx.message.account, location)
const isZero = stored.equals(Bytes32.ZERO)
const isZero = stored.iszero()
ctx.useGas(isZero ? GasCost.SSET : GasCost.SRESET)
if (!isZero && value.equals(Bytes32.ZERO)) {
if (!isZero && value.iszero()) {
ctx.refund(GasRefund.SCLEAR)
}

Expand Down
22 changes: 11 additions & 11 deletions packages/evm/test/Bytes32.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,19 @@ describe('Bytes32', () => {
it('fromNumber works for positive numbers', () => {
const a = Bytes32.fromNumber(42)
const b = Bytes32.fromHex('2a')
expect(a.equals(b)).to.equal(true)
expect(a.eq(b)).to.equal(true)
})

it('fromNumber works for zero', () => {
const a = Bytes32.fromNumber(0)
const b = Bytes32.ZERO
expect(a.equals(b)).to.equal(true)
expect(a.eq(b)).to.equal(true)
})

it('fromNumber works for negative numbers', () => {
const a = Bytes32.fromNumber(-42)
const b = Bytes32.ZERO.sub(Bytes32.fromNumber(42))
expect(a.equals(b)).to.equal(true)
expect(a.eq(b)).to.equal(true)
})

it('toUnsignedNumber returns a number', () => {
Expand All @@ -61,13 +61,13 @@ describe('Bytes32', () => {
it('fromHex works for small numbers', () => {
const a = Bytes32.fromHex('1')
const b = Bytes32.ONE
expect(a.equals(b)).to.equal(true)
expect(a.eq(b)).to.equal(true)
})

it('fromHex works for large numbers', () => {
const a = Bytes32.fromHex('f'.repeat(64))
const b = Bytes32.MAX
expect(a.equals(b)).to.equal(true)
expect(a.eq(b)).to.equal(true)
})

it('toHex pads zeroes at the start', () => {
Expand All @@ -79,26 +79,26 @@ describe('Bytes32', () => {
describe('fromBoolean', () => {
it('creates ZERO from false', () => {
const result = Bytes32.fromBoolean(false)
expect(result.equals(Bytes32.ZERO)).to.equal(true)
expect(result.iszero()).to.equal(true)
})

it('creates ONE from true', () => {
const result = Bytes32.fromBoolean(true)
expect(result.equals(Bytes32.ONE)).to.equal(true)
expect(result.eq(Bytes32.ONE)).to.equal(true)
})
})

describe('from and to bytes', () => {
it('fromBytes works for small numbers', () => {
const a = Bytes32.fromBytes([0x11, 0x22] as Byte[])
const b = Bytes32.fromHex('1122')
expect(a.equals(b)).to.equal(true)
expect(a.eq(b)).to.equal(true)
})

it('fromBytes works for large numbers', () => {
const a = Bytes32.fromBytes(new Array(32).fill(0xff))
const b = Bytes32.MAX
expect(a.equals(b)).to.equal(true)
expect(a.eq(b)).to.equal(true)
})

it('toBytes pads zeroes at the start', () => {
Expand All @@ -111,11 +111,11 @@ describe('Bytes32', () => {
it('equals returns true for equal MachineWords', () => {
const a = Bytes32.fromHex('6').div(Bytes32.fromHex('2'))
const b = Bytes32.fromHex('3')
expect(a.equals(b)).to.equal(true)
expect(a.eq(b)).to.equal(true)
})

it('equals returns false for unequal MachineWords', () => {
expect(Bytes32.ONE.equals(Bytes32.MAX)).to.equal(false)
expect(Bytes32.ONE.eq(Bytes32.MAX)).to.equal(false)
})
})
})
Expand Down
20 changes: 10 additions & 10 deletions packages/evm/test/State.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ describe('State', () => {
it('getBalance returns ZERO by default', () => {
const state = new State()
const result = state.getBalance(address)
expect(result.equals(Bytes32.ZERO)).to.equal(true)
expect(result.iszero()).to.equal(true)
})

it('getBalance returns previously set value', () => {
const state = new State()
state.setBalance(address, Bytes32.ONE)
const result = state.getBalance(address)
expect(result.equals(Bytes32.ONE)).to.equal(true)
expect(result.eq(Bytes32.ONE)).to.equal(true)
})

it('getNonce returns 0 by default', () => {
Expand All @@ -36,14 +36,14 @@ describe('State', () => {
it('getStorage returns ZERO by default', () => {
const state = new State()
const result = state.getStorage(address, Bytes32.ZERO)
expect(result.equals(Bytes32.ZERO)).to.equal(true)
expect(result.iszero()).to.equal(true)
})

it('getStorage returns previously set value', () => {
const state = new State()
state.setStorage(address, Bytes32.ZERO, Bytes32.ONE)
const result = state.getStorage(address, Bytes32.ZERO)
expect(result.equals(Bytes32.ONE)).to.equal(true)
expect(result.eq(Bytes32.ONE)).to.equal(true)
})

it('getCode returns [] by default', () => {
Expand Down Expand Up @@ -75,15 +75,15 @@ describe('State', () => {
clone.setStorage(address, Bytes32.ONE, Bytes32.ONE)
clone.setCode(address, [4, 5] as Byte[])

expect(state.getBalance(address).equals(Bytes32.ONE)).to.equal(true)
expect(state.getStorage(address, Bytes32.ZERO).equals(Bytes32.ONE)).to.equal(true)
expect(state.getStorage(address, Bytes32.ONE).equals(Bytes32.ZERO)).to.equal(true)
expect(state.getBalance(address).eq(Bytes32.ONE)).to.equal(true)
expect(state.getStorage(address, Bytes32.ZERO).eq(Bytes32.ONE)).to.equal(true)
expect(state.getStorage(address, Bytes32.ONE).iszero()).to.equal(true)
expect(state.getNonce(address)).to.equal(1)
expect(state.getCode(address)).to.deep.equal([1, 2, 3])

expect(clone.getBalance(address).equals(Bytes32.MAX)).to.equal(true)
expect(clone.getStorage(address, Bytes32.ZERO).equals(Bytes32.MAX)).to.equal(true)
expect(clone.getStorage(address, Bytes32.ONE).equals(Bytes32.ONE)).to.equal(true)
expect(clone.getBalance(address).eq(Bytes32.MAX)).to.equal(true)
expect(clone.getStorage(address, Bytes32.ZERO).eq(Bytes32.MAX)).to.equal(true)
expect(clone.getStorage(address, Bytes32.ONE).eq(Bytes32.ONE)).to.equal(true)
expect(clone.getNonce(address)).to.equal(2)
expect(clone.getCode(address)).to.deep.equal([4, 5])
})
Expand Down

0 comments on commit f54214b

Please sign in to comment.