diff --git a/nimbus.nimble b/nimbus.nimble index 55882e712..a774fc365 100644 --- a/nimbus.nimble +++ b/nimbus.nimble @@ -19,7 +19,8 @@ requires "nim >= 0.18.1", "asyncdispatch2", "eth_p2p", "eth_keyfile", - "eth_keys" + "eth_keys", + "https://github.com/status-im/nim-bncurve" proc buildBinary(name: string, srcDir = ".", lang = "c") = if not dirExists "build": mkDir "build" diff --git a/nimbus/vm/precompiles.nim b/nimbus/vm/precompiles.nim index 0c4141c59..52947081c 100644 --- a/nimbus/vm/precompiles.nim +++ b/nimbus/vm/precompiles.nim @@ -1,7 +1,7 @@ import ../vm_types, interpreter/[gas_meter, gas_costs], ../errors, stint, eth_keys, eth_common, chronicles, tables, macros, - message, math, nimcrypto + message, math, nimcrypto, bncurve/[fields, groups] type PrecompileAddresses = enum @@ -22,6 +22,29 @@ proc getSignature*(computation: BaseComputation): Signature = bytes[64..63] = computation.msg.data[96..128] # S result = initSignature(bytes) # Can raise +proc getPoint[T: G1|G2](t: typedesc[T], data: openarray[byte]): Point[T] = + when T is G1: + const nextOffset = 32 + var px, py: FQ + else: + const nextOffset = 64 + var px, py: FQ2 + if not px.fromBytes(data.toOpenArray(0, nextOffset - 1)): + raise newException(ValidationError, "Could not get point value") + if not py.fromBytes(data.toOpenArray(nextOffset, nextOffset * 2 - 1)): + raise newException(ValidationError, "Could not get point value") + if px.isZero() and py.isZero(): + result = T.zero() + else: + var ap: AffinePoint[T] + if not ap.init(px, py): + raise newException(ValidationError, "Point is not on curve") + result = ap.toJacobian() + +proc getFR(data: openarray[byte]): FR = + if not result.fromBytes(data): + raise newException(ValidationError, "Could not get FR value") + proc ecRecover*(computation: var BaseComputation) = computation.gasMeter.consumeGas( GasECRecover, @@ -36,7 +59,7 @@ proc ecRecover*(computation: var BaseComputation) = if sig.recoverSignatureKey(msgHash, pubKey) != EthKeysStatus.Success: raise newException(ValidationError, "Could not derive public key from computation") - + computation.rawOutput = @(pubKey.toCanonicalAddress()) debug "ECRecover precompile", derivedKey = pubKey.toCanonicalAddress() @@ -67,6 +90,85 @@ proc identity*(computation: var BaseComputation) = computation.rawOutput = computation.msg.data debug "Identity precompile", output = computation.rawOutput +proc bn256ecAdd*(computation: var BaseComputation) = + var + input: array[128, byte] + output: array[64, byte] + + # Padding data + let msglen = len(computation.msg.data) + let tocopy = if msglen < 128: msglen else: 128 + if tocopy > 0: + copyMem(addr input[0], addr computation.msg.data[0], tocopy) + + var p1 = G1.getPoint(input.toOpenArray(0, 63)) + var p2 = G1.getPoint(input.toOpenArray(64, 127)) + var apo = (p1 + p2).toAffine() + if isSome(apo): + let p = apo.get() + # we can discard here because we supply proper buffer + discard p.toBytes(output) + + # TODO: gas computation + # computation.gasMeter.consumeGas(gasFee, reason = "ecAdd Precompile") + computation.rawOutput = @output + +proc bn256ecMul*(computation: var BaseComputation) = + var + input: array[96, byte] + output: array[64, byte] + + # Padding data + let msglen = len(computation.msg.data) + let tocopy = if msglen < 96: msglen else: 96 + if tocopy > 0: + copyMem(addr input[0], addr computation.msg.data[0], tocopy) + + var p1 = G1.getPoint(input.toOpenArray(0, 63)) + var fr = getFR(input.toOpenArray(64, 95)) + var apo = (p1 * fr).toAffine() + if isSome(apo): + let p = apo.get() + # we can discard here because we supply buffer of proper size + discard p.toBytes(output) + + # TODO: gas computation + # computation.gasMeter.consumeGas(gasFee, reason="ecMul Precompile") + computation.rawOutput = @output + +proc bn256ecPairing*(computation: var BaseComputation) = + var output: array[32, byte] + + let msglen = len(computation.msg.data) + if msglen mod 192 != 0: + raise newException(ValidationError, "Invalid input length") + + if msglen == 0: + # we can discard here because we supply buffer of proper size + discard BNU256.one().toBytes(output) + else: + # Calculate number of pairing pairs + let count = msglen div 192 + # Pairing accumulator + var acc = FQ12.one() + + for i in 0..