Added WRC20 challenge example

This commit is contained in:
Yuriy Glukhov 2018-07-09 10:32:44 +03:00
parent 8ca996d94f
commit 7fb5a95ac4
4 changed files with 351 additions and 4 deletions

View File

@ -1,8 +1,295 @@
import macros
{.pragma: importwasm, importc, cdecl.}
{.push cdecl, importc.}
proc getCallDataSize*(): uint32 {.importwasm.}
proc useGas*(amount: int64)
## Subtracts an amount to the gas counter
##
## Parameters:
## `amount` - the amount to subtract to the gas counter
proc getAddress*(resultOffset: pointer)
## Gets address of currently executing account and loads it into memory at
## the given offset.
##
## Parameters:
## `resultOffset` the memory offset to load the address into (`address`)
proc getBalance*(addressOffset, resultOffset: pointer)
## Gets balance of the given account and loads it into memory at the given offset.
##
## Parameters:
## `addressOffset` the memory offset to load the address from (`address`)
# `resultOffset` the memory offset to load the balance into (`u128`)
proc getBlockHash*(number: int64, resultOffset: pointer)
## Gets the hash of one of the 256 most recent complete blocks.
##
## Parameters:
## `number` which block to load
## `resultOffset` the memory offset to load the hash into (`u256`)
proc call*(gas: int64, addressOffset, valueOffset, dataOffset: pointer,
dataLength: int32): int32
## Sends a message with arbitrary data to a given address path
##
## Parameters:
## `gas` **i64** the gas limit
## `addressOffset` the memory offset to load the address from (`address`)
## `valueOffset` the memory offset to load the value from (`u128`)
## `dataOffset` the memory offset to load data from (`bytes`)
## `dataLength` the length of data
## Returns:
## 0 on success, 1 on failure and 2 on `revert`
proc callDataCopy*(resultOffset: pointer, dataOffset, length: int32)
## Copies the input data in current environment to memory. This pertains to
## the input data passed with the message call instruction or transaction.
##
## Parameters:
## `resultOffset` the memory offset to load data into (`bytes`)
## `dataOffset` the offset in the input data
## `length` the length of data to copy
proc getCallDataSize*(): int32
## Get size of input data in current environment. This pertains to the input
## data passed with the message call instruction or transaction.
##
## Returns: call data size
proc callCode*(gas: int64, addressOffset, valueOffset, dataOffset: pointer,
dataLength: int32): int32
## Message-call into this account with an alternative account's code.
##
## Parameters:
## `gas` the gas limit
## `addressOffset` the memory offset to load the address from (`address`)
## `valueOffset` the memory offset to load the value from (`u128`)
## `dataOffset` the memory offset to load data from (`bytes`)
## `dataLength` the length of data
##
## Returns: 0 on success, 1 on failure and 2 on `revert`
proc callDelegate*(gas: int64, addressOffset, dataOffset: pointer,
dataLength: int32)
## Message-call into this account with an alternative accounts code, but
## persisting the current values for sender and value.
##
## Parameters:
## `gas` the gas limit
## `addressOffset` the memory offset to load the address from (`address`)
## `dataOffset` the memory offset to load data from (`bytes`)
## `dataLength` the length of data
##
## Returns: 0 on success, 1 on failure and 2 on `revert`
proc callStatic*(gas: int64, addressOffset, dataOffset: pointer,
dataLength: int32)
## Sends a message with arbitrary data to a given address path, but disallow
## state modifications. This includes `log`, `create`, `selfdestruct` and `call`
## with a non-zero value.
##
## Parameters:
## `gas` the gas limit
## `addressOffset` the memory offset to load the address from (`address`)
## `dataOffset` the memory offset to load data from (`bytes`)
## `dataLength` the length of data
##
## Returns: 0 on success, 1 on failure and 2 on `revert`
proc storageStore*(pathOffset, valueOffset: pointer)
## Store 256-bit a value in memory to persistent storage
##
## Parameters:
## `pathOffset` the memory offset to load the path from (`u256`)
## `valueOffset` the memory offset to load the value from (`u256`)
proc storageLoad*(pathOffset, valueOffset: pointer)
## Loads a 256-bit a value to memory from persistent storage
##
## Parameters:
## `pathOffset` the memory offset to load the path from (`u256`)
## `resultOffset` the memory offset to store the result at (`u256`)
proc getCaller*(resultOffset: pointer)
## Gets caller address and loads it into memory at the given offset. This is
## the address of the account that is directly responsible for this execution.
##
## Parameters:
## `resultOffset` the memory offset to load the address into (`address`)
proc getCallValue*(resultOffset: pointer)
## Gets the deposited value by the instruction/transaction responsible for
## this execution and loads it into memory at the given location.
##
## Parameters:
## `resultOffset` the memory offset to load the value into (`u128`)
proc codeCopy*(resultOffset: pointer, codeOffset, length: int32)
## Copies the code running in current environment to memory.
##
## Parameters:
## `resultOffset` the memory offset to load the result into (`bytes`)
## `codeOffset` the offset within the code
## `length` the length of code to copy
proc getCodeSize*(): int32
## Gets the size of code running in current environment.
proc getBlockCoinbase*(resultOffset: pointer)
## Gets the blocks beneficiary address and loads into memory.
##
## Parameters:
## `resultOffset` the memory offset to load the coinbase address into (`address`)
proc create*(valueOffset, dataOffset: pointer, length: int32, resultOffset: pointer): int32
## Creates a new contract with a given value.
##
## Parameters:
## `valueOffset` the memory offset to load the value from (`u128`)
## `dataOffset` the memory offset to load the code for the new contract from (`bytes`)
## `length` the data length
## `resultOffset` the memory offset to write the new contract address to (`address`)
##
## Note: `create` will clear the return buffer in case of success or may fill
## it with data coming from `revert`.
##
## Returns: 0 on success, 1 on failure and 2 on `revert`
proc getBlockDifficulty*(resultOffset: pointer)
## Get the blocks difficulty.
##
## Parameters:
## `resultOffset` the memory offset to load the difficulty into (`u256`)
proc externalCodeCopy*(addressOffset, resultOffset: pointer, codeOffset, length: int32)
## Copies the code of an account to memory.
##
## Parameters:
## `addressOffset` the memory offset to load the address from (`address`)
## `resultOffset` the memory offset to load the result into (`bytes`)
## `codeOffset` the offset within the code
## `length` the length of code to copy
proc getExternalCodeSize*(addressOffset: pointer): int32
## Get size of an accounts code.
##
## Parameters:
## `addressOffset` the memory offset to load the address from (`address`)
proc getGasLeft*(): int64
## Returns the current gasCounter
proc getBlockGasLimit*(): int64
## Get the blocks gas limit.
proc getTxGasPrice*(valueOffset: pointer)
## Gets price of gas in current environment.
##
## Parameters:
## `valueOffset` the memory offset to write the value to (`u128`)
proc log*(dataOffset: pointer, length, numberOfTopics: int32,
topic1, topic2, topic3, topic4: pointer)
## Creates a new log in the current environment
##
## Parameters:
## `dataOffset` the memory offset to load data from (`bytes`)
## `length` the data length
## `numberOfTopics` the number of topics following (0 to 4)
## `topic1` the memory offset to load topic1 from (`u256`)
## `topic2` the memory offset to load topic2 from (`u256`)
## `topic3` the memory offset to load topic3 from (`u256`)
## `topic4` the memory offset to load topic4 from (`u256`)
proc getBlockNumber*(): int64
## Get the blocks number.
proc getTxOrigin*(resultOffset: pointer)
## Gets the execution's origination address and loads it into memory at the
## given offset. This is the sender of original transaction; it is never an
## account with non-empty associated code.
##
## Parameters:
## `resultOffset` the memory offset to load the origin address from (`address`)
proc ret*(dataOffset: pointer, length: int32) {.
importc: "retAux", codegenDecl: "$# $#$# __asm__(\"return\")".}
## Set the returning output data for the execution.
##
## Note: multiple invocations will overwrite the previous data.
##
## Parameters:
## `dataOffset` the memory offset of the output data (`bytes`)
## `length` the length of the output data
proc revert*(dataOffset: pointer, length: int32)
## Set the returning output data for the execution.
##
## Note: multiple invocations will overwrite the previous data.
##
## Parameters:
## `dataOffset` the memory offset of the output data (`bytes`)
## `length` the length of the output data
proc getReturnDataSize*(): int32
## Get size of current return data buffer to memory. This contains the return
## data from the last executed `call`, `callCode`, `callDelegate`, `callStatic`
## or `create`.
##
## Note: `create` only fills the return data buffer in case of a failure.
proc returnDataCopy*(resultOffset: pointer, dataOffset, length: int32)
## Copies the current return data buffer to memory. This contains the return data
## from last executed `call`, `callCode`, `callDelegate`, `callStatic` or `create`.
##
## Note: `create` only fills the return data buffer in case of a failure.
##
## Parameters:
## `resultOffset` the memory offset to load data into (`bytes`)
## `dataOffset` the offset in the return data
## `length` the length of data to copy
proc selfDestruct*(addressOffset: pointer)
## Mark account for later deletion and give the remaining balance to the specified
## beneficiary address. This takes effect once the contract execution terminates.
##
## Note: multiple invocations will overwrite the benficiary address.
##
## Note: the contract **shall** halt execution after this call.
##
## Parameters:
## `addressOffset` the memory offset to load the address from (`address`)
proc getBlockTimestamp*(): int64
## Get the blocks timestamp.
{.pop.}
proc callDataCopy*[T](res: var T, offset: int) {.inline.} =
callDataCopy(addr res, offset.int32, sizeof(res).int32)
proc storageLoad*[N](path: array[N, byte], res: pointer) {.inline.} =
when path.len < 32:
type PaddedPath {.packed.} = object
padding: array[32 - path.len, byte]
p: array[N, byte]
var p: PaddedPath
p.p = path
storageLoad(addr p, res)
else:
storageLoad(unsafeAddr path[0], res)
proc storageStore*[N](path: array[N, byte], res: pointer) {.inline.} =
when path.len < 32:
type PaddedPath {.packed.} = object
padding: array[32 - path.len, byte]
p: array[N, byte]
var p: PaddedPath
p.p = path
storageStore(addr p, res)
else:
storageStore(unsafeAddr path[0], res)
macro exportwasm*(p: untyped): untyped =
expectKind(p, nnkProcDef)

View File

@ -1,4 +1,56 @@
## ewasm “WRC20” token contract coding challenge
## https://gist.github.com/axic/16158c5c88fbc7b1d09dfa8c658bc363
import ../eth_contracts
proc test(): int32 {.exportwasm.} =
let sz = getCallDataSize().int32
proc do_balance() =
if getCallDataSize() != 24:
revert(nil, 0)
var address: array[20, byte]
callDataCopy(address, 4)
var balance: array[32, byte]
storageLoad(address, addr balance)
ret(addr balance, sizeof(balance).int32)
proc do_transfer() =
if getCallDataSize() != 32:
revert(nil, 0)
var sender: array[20, byte]
getCaller(addr sender)
var recipient: array[20, byte]
callDataCopy(recipient, 4)
var value: uint64
callDataCopy(value, 24)
var senderBalance: array[32, byte]
storageLoad(sender, addr senderBalance)
var recipientBalance: array[32, byte]
storageLoad(recipient, addr recipientBalance)
block:
# This is a quick stub to avoid using stint for now. Works only with
# single byte values.
if sender_balance[31].uint64 < value:
revert(nil, 0)
sender_balance[31] -= value.byte
recipient_balance[31] += value.byte
storageStore(sender, addr senderBalance)
storageStore(recipient, addr recipientBalance)
proc main() {.exportwasm.} =
if getCallDataSize() < 4:
revert(nil, 0)
var selector: uint32
callDataCopy(selector, 0)
case selector
of 0x9993021a'u32:
do_balance()
of 0x5d359fbd'u32:
do_transfer()
else:
revert(nil, 0)

View File

@ -15,6 +15,9 @@ let llTarget = "wasm32-unknown-unknown-wasm"
switch("passC", "--target=" & llTarget)
switch("passL", "--target=" & llTarget)
switch("passC", "-I./include")
switch("clang.exe", llBin & "/clang")
switch("clang.linkerexe", llBin & "/clang")
switch("clang.options.linker", "-nostdlib -Wl,--no-entry,--allow-undefined,--strip-all")

5
include/string.h Normal file
View File

@ -0,0 +1,5 @@
/*
This is a stub file to suppress compilation errors, when nim calls out to
memcpy, memset and memcpy. Currently we rely on clang optimizing these calls
away, but in future we will likely have to come up with something better/reliable
*/