CJentzsch
10 years ago
319 changed files with 28260 additions and 6701 deletions
@ -1,27 +0,0 @@ |
|||
#!/usr/bin/python |
|||
# cpp-ethereum build script |
|||
# to be used from CI server, or to build locally |
|||
# uses python instead of bash script for better cross-platform support |
|||
|
|||
# TODO Initial version. Needs much more improvements |
|||
|
|||
import argparse |
|||
import os |
|||
import subprocess |
|||
|
|||
def build_dependencies(): |
|||
if os.path.exists("extdep"): |
|||
os.chdir("extdep") |
|||
if not os.path.exists("build"): |
|||
os.makedirs("build") |
|||
os.chdir("build") |
|||
subprocess.check_call(["cmake", ".."]) |
|||
subprocess.check_call("make") |
|||
|
|||
parser = argparse.ArgumentParser() |
|||
parser.add_argument("cmd", help="what to build") |
|||
|
|||
args = parser.parse_args() |
|||
if args.cmd == "dep": |
|||
build_dependencies() |
|||
|
@ -0,0 +1,38 @@ |
|||
#pragma once |
|||
|
|||
#include <evmjit/libevmjit/Common.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
|
|||
inline u256 llvm2eth(jit::i256 _i) |
|||
{ |
|||
u256 u = 0; |
|||
u |= _i.d; |
|||
u <<= 64; |
|||
u |= _i.c; |
|||
u <<= 64; |
|||
u |= _i.b; |
|||
u <<= 64; |
|||
u |= _i.a; |
|||
return u; |
|||
} |
|||
|
|||
inline jit::i256 eth2llvm(u256 _u) |
|||
{ |
|||
jit::i256 i; |
|||
u256 mask = 0xFFFFFFFFFFFFFFFF; |
|||
i.a = static_cast<uint64_t>(_u & mask); |
|||
_u >>= 64; |
|||
i.b = static_cast<uint64_t>(_u & mask); |
|||
_u >>= 64; |
|||
i.c = static_cast<uint64_t>(_u & mask); |
|||
_u >>= 64; |
|||
i.d = static_cast<uint64_t>(_u & mask); |
|||
return i; |
|||
} |
|||
|
|||
} |
|||
} |
@ -1,237 +1,239 @@ |
|||
#include "Memory.h" |
|||
|
|||
#include <vector> |
|||
#include <iostream> |
|||
#include <iomanip> |
|||
#include <cstdint> |
|||
#include <cassert> |
|||
|
|||
#include <llvm/IR/GlobalVariable.h> |
|||
#include <llvm/IR/Function.h> |
|||
#include <llvm/IR/IntrinsicInst.h> |
|||
|
|||
#include "Type.h" |
|||
#include "Runtime.h" |
|||
#include "GasMeter.h" |
|||
#include "Endianness.h" |
|||
#include "RuntimeManager.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): |
|||
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
|
|||
m_gasMeter(_gasMeter) |
|||
{ |
|||
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr}; |
|||
m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule()); |
|||
llvm::AttrBuilder attrBuilder; |
|||
attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly); |
|||
m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder)); |
|||
|
|||
m_require = createRequireFunc(_gasMeter); |
|||
m_loadWord = createFunc(false, Type::Word, _gasMeter); |
|||
m_storeWord = createFunc(true, Type::Word, _gasMeter); |
|||
m_storeByte = createFunc(true, Type::Byte, _gasMeter); |
|||
} |
|||
|
|||
llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter) |
|||
{ |
|||
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word}; |
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); |
|||
auto rt = func->arg_begin(); |
|||
rt->setName("rt"); |
|||
auto offset = rt->getNextNode(); |
|||
offset->setName("offset"); |
|||
auto size = offset->getNextNode(); |
|||
size->setName("size"); |
|||
|
|||
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); |
|||
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); |
|||
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); |
|||
auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); |
|||
|
|||
InsertPointGuard guard(m_builder); // Restores insert point at function exit
|
|||
|
|||
// BB "Pre": Ignore checks with size 0
|
|||
m_builder.SetInsertPoint(preBB); |
|||
auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0)); |
|||
m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB); |
|||
|
|||
// BB "Check"
|
|||
m_builder.SetInsertPoint(checkBB); |
|||
auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word); |
|||
auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res"); |
|||
auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq"); |
|||
auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1"); |
|||
auto rtPtr = getRuntimeManager().getRuntimePtr(); |
|||
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); |
|||
auto currSize = m_builder.CreateLoad(sizePtr, "currSize"); |
|||
auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall"); |
|||
auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded"); |
|||
m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights?
|
|||
|
|||
// BB "Resize"
|
|||
m_builder.SetInsertPoint(resizeBB); |
|||
// Check gas first
|
|||
uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res"); |
|||
auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0); |
|||
auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2"); |
|||
auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow"); |
|||
wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired); |
|||
wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq"); |
|||
sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq"); |
|||
auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k
|
|||
auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); |
|||
_gasMeter.countMemory(newWords); |
|||
// Resize
|
|||
m_builder.CreateStore(sizeRequired, sizePtr); |
|||
auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData"); |
|||
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); |
|||
m_builder.CreateStore(newData, dataPtr); |
|||
m_builder.CreateBr(returnBB); |
|||
|
|||
// BB "Return"
|
|||
m_builder.SetInsertPoint(returnBB); |
|||
m_builder.CreateRetVoid(); |
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&) |
|||
{ |
|||
auto isWord = _valueType == Type::Word; |
|||
|
|||
llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType}; |
|||
llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word}; |
|||
auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; |
|||
auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false); |
|||
auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule()); |
|||
|
|||
InsertPointGuard guard(m_builder); // Restores insert point at function exit
|
|||
|
|||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); |
|||
auto rt = func->arg_begin(); |
|||
rt->setName("rt"); |
|||
auto index = rt->getNextNode(); |
|||
index->setName("index"); |
|||
|
|||
auto valueSize = _valueType->getPrimitiveSizeInBits() / 8; |
|||
this->require(index, Constant::get(valueSize)); |
|||
auto ptr = getBytePtr(index); |
|||
if (isWord) |
|||
ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr"); |
|||
if (_isStore) |
|||
{ |
|||
llvm::Value* value = index->getNextNode(); |
|||
value->setName("value"); |
|||
if (isWord) |
|||
value = Endianness::toBE(m_builder, value); |
|||
m_builder.CreateStore(value, ptr); |
|||
m_builder.CreateRetVoid(); |
|||
} |
|||
else |
|||
{ |
|||
llvm::Value* ret = m_builder.CreateLoad(ptr); |
|||
ret = Endianness::toNative(m_builder, ret); |
|||
m_builder.CreateRet(ret); |
|||
} |
|||
|
|||
return func; |
|||
} |
|||
|
|||
|
|||
llvm::Value* Memory::loadWord(llvm::Value* _addr) |
|||
{ |
|||
return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr}); |
|||
} |
|||
|
|||
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) |
|||
{ |
|||
createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word}); |
|||
} |
|||
|
|||
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) |
|||
{ |
|||
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); |
|||
createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte}); |
|||
} |
|||
|
|||
llvm::Value* Memory::getData() |
|||
{ |
|||
auto rtPtr = getRuntimeManager().getRuntimePtr(); |
|||
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); |
|||
return m_builder.CreateLoad(dataPtr, "data"); |
|||
} |
|||
|
|||
llvm::Value* Memory::getSize() |
|||
{ |
|||
auto rtPtr = getRuntimeManager().getRuntimePtr(); |
|||
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); |
|||
return m_builder.CreateLoad(sizePtr, "size"); |
|||
} |
|||
|
|||
llvm::Value* Memory::getBytePtr(llvm::Value* _index) |
|||
{ |
|||
return m_builder.CreateGEP(getData(), _index, "ptr"); |
|||
} |
|||
|
|||
void Memory::require(llvm::Value* _offset, llvm::Value* _size) |
|||
{ |
|||
createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size}); |
|||
} |
|||
|
|||
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, |
|||
llvm::Value* _destMemIdx, llvm::Value* _reqBytes) |
|||
{ |
|||
require(_destMemIdx, _reqBytes); |
|||
|
|||
// Additional copy cost
|
|||
// TODO: This round ups to 32 happens in many places
|
|||
auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32)); |
|||
m_gasMeter.countCopy(copyWords); |
|||
|
|||
// Algorithm:
|
|||
// isOutsideData = idx256 >= size256
|
|||
// idx64 = trunc idx256
|
|||
// size64 = trunc size256
|
|||
// dataLeftSize = size64 - idx64 // safe if not isOutsideData
|
|||
// reqBytes64 = trunc _reqBytes // require() handles large values
|
|||
// bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min
|
|||
// bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
|
|||
|
|||
auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); |
|||
auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision); |
|||
auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision); |
|||
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); |
|||
auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision); |
|||
auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize); |
|||
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64); |
|||
auto zero64 = llvm::ConstantInt::get(Type::lowPrecision, 0); // TODO: Cache common constants
|
|||
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, zero64, bytesToCopyInner); |
|||
|
|||
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); |
|||
auto dst = m_builder.CreateGEP(getData(), _destMemIdx, "dst"); |
|||
m_builder.CreateMemCpy(dst, src, bytesToCopy, 0); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
extern "C" |
|||
{ |
|||
using namespace dev::eth::jit; |
|||
|
|||
EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR
|
|||
{ |
|||
auto size = _size->a; // Trunc to 64-bit
|
|||
auto& memory = _rt->getMemory(); |
|||
memory.resize(size); |
|||
return memory.data(); |
|||
} |
|||
} |
|||
#include "Memory.h" |
|||
|
|||
#include <vector> |
|||
#include <iostream> |
|||
#include <iomanip> |
|||
#include <cstdint> |
|||
#include <cassert> |
|||
|
|||
#include <llvm/IR/GlobalVariable.h> |
|||
#include <llvm/IR/Function.h> |
|||
#include <llvm/IR/IntrinsicInst.h> |
|||
|
|||
#include "Type.h" |
|||
#include "Runtime.h" |
|||
#include "GasMeter.h" |
|||
#include "Endianness.h" |
|||
#include "RuntimeManager.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): |
|||
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
|
|||
m_gasMeter(_gasMeter) |
|||
{ |
|||
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr}; |
|||
m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule()); |
|||
llvm::AttrBuilder attrBuilder; |
|||
attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly); |
|||
m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder)); |
|||
|
|||
m_require = createRequireFunc(_gasMeter); |
|||
m_loadWord = createFunc(false, Type::Word, _gasMeter); |
|||
m_storeWord = createFunc(true, Type::Word, _gasMeter); |
|||
m_storeByte = createFunc(true, Type::Byte, _gasMeter); |
|||
} |
|||
|
|||
llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter) |
|||
{ |
|||
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word}; |
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); |
|||
auto rt = func->arg_begin(); |
|||
rt->setName("rt"); |
|||
auto offset = rt->getNextNode(); |
|||
offset->setName("offset"); |
|||
auto size = offset->getNextNode(); |
|||
size->setName("size"); |
|||
|
|||
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); |
|||
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); |
|||
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); |
|||
auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); |
|||
|
|||
InsertPointGuard guard(m_builder); // Restores insert point at function exit
|
|||
|
|||
// BB "Pre": Ignore checks with size 0
|
|||
m_builder.SetInsertPoint(preBB); |
|||
auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0)); |
|||
m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB); |
|||
|
|||
// BB "Check"
|
|||
m_builder.SetInsertPoint(checkBB); |
|||
auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word); |
|||
auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res"); |
|||
auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq"); |
|||
auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1"); |
|||
auto rtPtr = getRuntimeManager().getRuntimePtr(); |
|||
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); |
|||
auto currSize = m_builder.CreateLoad(sizePtr, "currSize"); |
|||
auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall"); |
|||
auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded"); |
|||
m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights?
|
|||
|
|||
// BB "Resize"
|
|||
m_builder.SetInsertPoint(resizeBB); |
|||
// Check gas first
|
|||
uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res"); |
|||
auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0); |
|||
auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2"); |
|||
auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow"); |
|||
wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired); |
|||
wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq"); |
|||
sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq"); |
|||
auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k
|
|||
auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); |
|||
_gasMeter.countMemory(newWords); |
|||
// Resize
|
|||
m_builder.CreateStore(sizeRequired, sizePtr); |
|||
auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData"); |
|||
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); |
|||
m_builder.CreateStore(newData, dataPtr); |
|||
m_builder.CreateBr(returnBB); |
|||
|
|||
// BB "Return"
|
|||
m_builder.SetInsertPoint(returnBB); |
|||
m_builder.CreateRetVoid(); |
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&) |
|||
{ |
|||
auto isWord = _valueType == Type::Word; |
|||
|
|||
llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType}; |
|||
llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word}; |
|||
auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; |
|||
auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false); |
|||
auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule()); |
|||
|
|||
InsertPointGuard guard(m_builder); // Restores insert point at function exit
|
|||
|
|||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); |
|||
auto rt = func->arg_begin(); |
|||
rt->setName("rt"); |
|||
auto index = rt->getNextNode(); |
|||
index->setName("index"); |
|||
|
|||
auto valueSize = _valueType->getPrimitiveSizeInBits() / 8; |
|||
this->require(index, Constant::get(valueSize)); |
|||
auto ptr = getBytePtr(index); |
|||
if (isWord) |
|||
ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr"); |
|||
if (_isStore) |
|||
{ |
|||
llvm::Value* value = index->getNextNode(); |
|||
value->setName("value"); |
|||
if (isWord) |
|||
value = Endianness::toBE(m_builder, value); |
|||
m_builder.CreateStore(value, ptr); |
|||
m_builder.CreateRetVoid(); |
|||
} |
|||
else |
|||
{ |
|||
llvm::Value* ret = m_builder.CreateLoad(ptr); |
|||
ret = Endianness::toNative(m_builder, ret); |
|||
m_builder.CreateRet(ret); |
|||
} |
|||
|
|||
return func; |
|||
} |
|||
|
|||
|
|||
llvm::Value* Memory::loadWord(llvm::Value* _addr) |
|||
{ |
|||
return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr}); |
|||
} |
|||
|
|||
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) |
|||
{ |
|||
createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word}); |
|||
} |
|||
|
|||
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) |
|||
{ |
|||
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); |
|||
createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte}); |
|||
} |
|||
|
|||
llvm::Value* Memory::getData() |
|||
{ |
|||
auto rtPtr = getRuntimeManager().getRuntimePtr(); |
|||
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); |
|||
return m_builder.CreateLoad(dataPtr, "data"); |
|||
} |
|||
|
|||
llvm::Value* Memory::getSize() |
|||
{ |
|||
auto rtPtr = getRuntimeManager().getRuntimePtr(); |
|||
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); |
|||
return m_builder.CreateLoad(sizePtr, "size"); |
|||
} |
|||
|
|||
llvm::Value* Memory::getBytePtr(llvm::Value* _index) |
|||
{ |
|||
auto idx = m_builder.CreateTrunc(_index, Type::Size, "idx"); // Never allow memory index be a type bigger than i64
|
|||
return m_builder.CreateGEP(getData(), idx, "ptr"); |
|||
} |
|||
|
|||
void Memory::require(llvm::Value* _offset, llvm::Value* _size) |
|||
{ |
|||
createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size}); |
|||
} |
|||
|
|||
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, |
|||
llvm::Value* _destMemIdx, llvm::Value* _reqBytes) |
|||
{ |
|||
require(_destMemIdx, _reqBytes); |
|||
|
|||
// Additional copy cost
|
|||
// TODO: This round ups to 32 happens in many places
|
|||
auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32)); |
|||
m_gasMeter.countCopy(copyWords); |
|||
|
|||
// Algorithm:
|
|||
// isOutsideData = idx256 >= size256
|
|||
// idx64 = trunc idx256
|
|||
// size64 = trunc size256
|
|||
// dataLeftSize = size64 - idx64 // safe if not isOutsideData
|
|||
// reqBytes64 = trunc _reqBytes // require() handles large values
|
|||
// bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min
|
|||
// bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
|
|||
|
|||
auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); |
|||
auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision); |
|||
auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision); |
|||
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); |
|||
auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision); |
|||
auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize); |
|||
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64); |
|||
auto zero64 = llvm::ConstantInt::get(Type::lowPrecision, 0); // TODO: Cache common constants
|
|||
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, zero64, bytesToCopyInner); |
|||
|
|||
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); |
|||
auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64
|
|||
auto dst = m_builder.CreateGEP(getData(), dstIdx, "dst"); |
|||
m_builder.CreateMemCpy(dst, src, bytesToCopy, 0); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
extern "C" |
|||
{ |
|||
using namespace dev::eth::jit; |
|||
|
|||
EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR
|
|||
{ |
|||
auto size = _size->a; // Trunc to 64-bit
|
|||
auto& memory = _rt->getMemory(); |
|||
memory.resize(size); |
|||
return memory.data(); |
|||
} |
|||
} |
|||
|
@ -1,59 +1,46 @@ |
|||
|
|||
#pragma once |
|||
|
|||
#include <csetjmp> |
|||
#include <vector> |
|||
|
|||
#include "Instruction.h" |
|||
#include "CompilerHelper.h" |
|||
#include "Utils.h" |
|||
#include "Type.h" |
|||
#include "RuntimeData.h" |
|||
|
|||
|
|||
#ifdef _MSC_VER |
|||
#define EXPORT __declspec(dllexport) |
|||
#else |
|||
#define EXPORT |
|||
#endif |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
using StackImpl = std::vector<i256>; |
|||
using MemoryImpl = bytes; |
|||
using jmp_buf_ref = decltype(&std::jmp_buf{}[0]); |
|||
|
|||
class Runtime |
|||
{ |
|||
public: |
|||
Runtime(RuntimeData* _data, Env* _env); |
|||
|
|||
Runtime(const Runtime&) = delete; |
|||
Runtime& operator=(const Runtime&) = delete; |
|||
|
|||
StackImpl& getStack() { return m_stack; } |
|||
MemoryImpl& getMemory() { return m_memory; } |
|||
Env* getEnvPtr() { return &m_env; } |
|||
|
|||
bytes getReturnData() const; |
|||
jmp_buf_ref getJmpBuf() { return m_jmpBuf; } |
|||
|
|||
private: |
|||
RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract.
|
|||
Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract.
|
|||
jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract.
|
|||
byte* m_memoryData = nullptr; |
|||
i256 m_memorySize; |
|||
std::jmp_buf m_jmpBuf; |
|||
StackImpl m_stack; |
|||
MemoryImpl m_memory; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
#pragma once |
|||
|
|||
#include <csetjmp> |
|||
#include "RuntimeData.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
using StackImpl = std::vector<i256>; |
|||
using MemoryImpl = bytes; |
|||
using jmp_buf_ref = decltype(&std::jmp_buf{}[0]); |
|||
|
|||
class Runtime |
|||
{ |
|||
public: |
|||
Runtime(RuntimeData* _data, Env* _env); |
|||
|
|||
Runtime(const Runtime&) = delete; |
|||
Runtime& operator=(const Runtime&) = delete; |
|||
|
|||
StackImpl& getStack() { return m_stack; } |
|||
MemoryImpl& getMemory() { return m_memory; } |
|||
Env* getEnvPtr() { return &m_env; } |
|||
|
|||
bytes_ref getReturnData() const; |
|||
jmp_buf_ref getJmpBuf() { return m_jmpBuf; } |
|||
|
|||
private: |
|||
RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract.
|
|||
Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract.
|
|||
jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract.
|
|||
byte* m_memoryData = nullptr; |
|||
i256 m_memorySize; |
|||
std::jmp_buf m_jmpBuf; |
|||
StackImpl m_stack; |
|||
MemoryImpl m_memory; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
@ -1,34 +1,41 @@ |
|||
#include "interface.h" |
|||
#include <cstdio> |
|||
#include "ExecutionEngine.h" |
|||
|
|||
extern "C" |
|||
{ |
|||
|
|||
evmjit_result evmjit_run(void* _data, void* _env) |
|||
{ |
|||
using namespace dev::eth::jit; |
|||
using namespace dev::eth::jit; |
|||
|
|||
auto data = static_cast<RuntimeData*>(_data); |
|||
#ifdef _MSC_VER |
|||
#define _ALLOW_KEYWORD_MACROS |
|||
#define noexcept throw() |
|||
#endif |
|||
|
|||
ExecutionEngine engine; |
|||
EXPORT void* evmjit_create() noexcept |
|||
{ |
|||
return new(std::nothrow) ExecutionEngine; |
|||
} |
|||
|
|||
EXPORT void evmjit_destroy(ExecutionEngine* _engine) noexcept |
|||
{ |
|||
delete _engine; |
|||
} |
|||
|
|||
auto codePtr = data->code; |
|||
auto codeSize = data->elems[RuntimeData::CodeSize].a; |
|||
bytes bytecode; |
|||
bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize); |
|||
EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept |
|||
{ |
|||
try |
|||
{ |
|||
auto codePtr = _data->code; |
|||
auto codeSize = _data->codeSize; |
|||
bytes bytecode; |
|||
bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize); |
|||
|
|||
auto returnCode = engine.run(bytecode, data, static_cast<Env*>(_env)); |
|||
evmjit_result result = {static_cast<int32_t>(returnCode), 0, nullptr}; |
|||
if (returnCode == ReturnCode::Return && !engine.returnData.empty()) |
|||
auto returnCode = _engine->run(bytecode, _data, _env); |
|||
return static_cast<int>(returnCode); |
|||
} |
|||
catch(...) |
|||
{ |
|||
// TODO: Optimized returning data. Allocating memory on client side by callback function might be a good idea
|
|||
result.returnDataSize = engine.returnData.size(); |
|||
result.returnData = std::malloc(result.returnDataSize); |
|||
std::memcpy(result.returnData, engine.returnData.data(), result.returnDataSize); |
|||
return static_cast<int>(ReturnCode::UnexpectedException); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
} |
|||
|
@ -0,0 +1,86 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file CanonBlockChain.cpp
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "CanonBlockChain.h" |
|||
|
|||
#include <boost/filesystem.hpp> |
|||
#include <libdevcore/Common.h> |
|||
#include <libdevcore/RLP.h> |
|||
#include <libdevcrypto/FileSystem.h> |
|||
#include <libethcore/Exceptions.h> |
|||
#include <libethcore/ProofOfWork.h> |
|||
#include <libethcore/BlockInfo.h> |
|||
#include <liblll/Compiler.h> |
|||
#include "State.h" |
|||
#include "Defaults.h" |
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
|
|||
#define ETH_CATCH 1 |
|||
|
|||
std::map<Address, Account> const& dev::eth::genesisState() |
|||
{ |
|||
static std::map<Address, Account> s_ret; |
|||
if (s_ret.empty()) |
|||
{ |
|||
// Initialise.
|
|||
for (auto i: vector<string>({ |
|||
"dbdbdb2cbd23b783741e8d7fcf51e459b497e4a6", |
|||
"e6716f9544a56c530d868e4bfbacb172315bdead", |
|||
"b9c015918bdaba24b4ff057a92a3873d6eb201be", |
|||
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4", |
|||
"2ef47100e0787b915105fd5e3f4ff6752079d5cb", |
|||
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826", |
|||
"6c386a4b26f73c802f34673f7248bb118f97424a", |
|||
"e4157b34ea9615cfbde6b4fda419828124b70c78" |
|||
})) |
|||
s_ret[Address(fromHex(i))] = Account(u256(1) << 200, Account::NormalCreation); |
|||
} |
|||
return s_ret; |
|||
} |
|||
|
|||
std::unique_ptr<BlockInfo> CanonBlockChain::s_genesis; |
|||
boost::shared_mutex CanonBlockChain::x_genesis; |
|||
|
|||
bytes CanonBlockChain::createGenesisBlock() |
|||
{ |
|||
RLPStream block(3); |
|||
|
|||
h256 stateRoot; |
|||
{ |
|||
MemoryDB db; |
|||
TrieDB<Address, MemoryDB> state(&db); |
|||
state.init(); |
|||
dev::eth::commit(genesisState(), db, state); |
|||
stateRoot = state.root(); |
|||
} |
|||
|
|||
block.appendList(14) |
|||
<< h256() << EmptyListSHA3 << h160() << stateRoot << EmptyTrie << EmptyTrie << LogBloom() << c_genesisDifficulty << 0 << 1000000 << 0 << (unsigned)0 << string() << sha3(bytes(1, 42)); |
|||
block.appendRaw(RLPEmptyList); |
|||
block.appendRaw(RLPEmptyList); |
|||
return block.out(); |
|||
} |
|||
|
|||
CanonBlockChain::CanonBlockChain(std::string _path, bool _killExisting): BlockChain(CanonBlockChain::createGenesisBlock(), _path, _killExisting) |
|||
{ |
|||
} |
@ -0,0 +1,76 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file CanonBlockChain.h
|
|||
* @author Gav Wood <i@gavwood.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#pragma warning(push) |
|||
#pragma warning(disable: 4100 4267) |
|||
#include <leveldb/db.h> |
|||
#pragma warning(pop) |
|||
|
|||
#include <mutex> |
|||
#include <libdevcore/Log.h> |
|||
#include <libdevcore/Exceptions.h> |
|||
#include <libethcore/CommonEth.h> |
|||
#include <libethcore/BlockInfo.h> |
|||
#include <libdevcore/Guards.h> |
|||
#include "BlockDetails.h" |
|||
#include "Account.h" |
|||
#include "BlockQueue.h" |
|||
#include "BlockChain.h" |
|||
namespace ldb = leveldb; |
|||
|
|||
namespace dev |
|||
{ |
|||
|
|||
namespace eth |
|||
{ |
|||
|
|||
// TODO: Move all this Genesis stuff into Genesis.h/.cpp
|
|||
std::map<Address, Account> const& genesisState(); |
|||
|
|||
/**
|
|||
* @brief Implements the blockchain database. All data this gives is disk-backed. |
|||
* @threadsafe |
|||
* @todo Make not memory hog (should actually act as a cache and deallocate old entries). |
|||
*/ |
|||
class CanonBlockChain: public BlockChain |
|||
{ |
|||
public: |
|||
CanonBlockChain(bool _killExisting = false): CanonBlockChain(std::string(), _killExisting) {} |
|||
CanonBlockChain(std::string _path, bool _killExisting = false); |
|||
~CanonBlockChain() {} |
|||
|
|||
/// @returns the genesis block header.
|
|||
static BlockInfo const& genesis() { UpgradableGuard l(x_genesis); if (!s_genesis) { auto gb = createGenesisBlock(); UpgradeGuard ul(l); s_genesis.reset(new BlockInfo); s_genesis->populate(&gb); } return *s_genesis; } |
|||
|
|||
/// @returns the genesis block as its RLP-encoded byte array.
|
|||
/// @note This is slow as it's constructed anew each call. Consider genesis() instead.
|
|||
static bytes createGenesisBlock(); |
|||
|
|||
private: |
|||
/// Static genesis info and its lock.
|
|||
static boost::shared_mutex x_genesis; |
|||
static std::unique_ptr<BlockInfo> s_genesis; |
|||
}; |
|||
|
|||
} |
|||
} |
File diff suppressed because one or more lines are too long
@ -1,966 +0,0 @@ |
|||
/*! |
|||
* @overview es6-promise - a tiny implementation of Promises/A+. |
|||
* @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) |
|||
* @license Licensed under MIT license |
|||
* See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE
|
|||
* @version 2.0.0 |
|||
*/ |
|||
|
|||
(function() { |
|||
"use strict"; |
|||
|
|||
function $$utils$$objectOrFunction(x) { |
|||
return typeof x === 'function' || (typeof x === 'object' && x !== null); |
|||
} |
|||
|
|||
function $$utils$$isFunction(x) { |
|||
return typeof x === 'function'; |
|||
} |
|||
|
|||
function $$utils$$isMaybeThenable(x) { |
|||
return typeof x === 'object' && x !== null; |
|||
} |
|||
|
|||
var $$utils$$_isArray; |
|||
|
|||
if (!Array.isArray) { |
|||
$$utils$$_isArray = function (x) { |
|||
return Object.prototype.toString.call(x) === '[object Array]'; |
|||
}; |
|||
} else { |
|||
$$utils$$_isArray = Array.isArray; |
|||
} |
|||
|
|||
var $$utils$$isArray = $$utils$$_isArray; |
|||
var $$utils$$now = Date.now || function() { return new Date().getTime(); }; |
|||
function $$utils$$F() { } |
|||
|
|||
var $$utils$$o_create = (Object.create || function (o) { |
|||
if (arguments.length > 1) { |
|||
throw new Error('Second argument not supported'); |
|||
} |
|||
if (typeof o !== 'object') { |
|||
throw new TypeError('Argument must be an object'); |
|||
} |
|||
$$utils$$F.prototype = o; |
|||
return new $$utils$$F(); |
|||
}); |
|||
|
|||
var $$asap$$len = 0; |
|||
|
|||
var $$asap$$default = function asap(callback, arg) { |
|||
$$asap$$queue[$$asap$$len] = callback; |
|||
$$asap$$queue[$$asap$$len + 1] = arg; |
|||
$$asap$$len += 2; |
|||
if ($$asap$$len === 2) { |
|||
// If len is 1, that means that we need to schedule an async flush.
|
|||
// If additional callbacks are queued before the queue is flushed, they
|
|||
// will be processed by this flush that we are scheduling.
|
|||
$$asap$$scheduleFlush(); |
|||
} |
|||
}; |
|||
|
|||
var $$asap$$browserGlobal = (typeof window !== 'undefined') ? window : {}; |
|||
var $$asap$$BrowserMutationObserver = $$asap$$browserGlobal.MutationObserver || $$asap$$browserGlobal.WebKitMutationObserver; |
|||
|
|||
// test for web worker but not in IE10
|
|||
var $$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' && |
|||
typeof importScripts !== 'undefined' && |
|||
typeof MessageChannel !== 'undefined'; |
|||
|
|||
// node
|
|||
function $$asap$$useNextTick() { |
|||
return function() { |
|||
process.nextTick($$asap$$flush); |
|||
}; |
|||
} |
|||
|
|||
function $$asap$$useMutationObserver() { |
|||
var iterations = 0; |
|||
var observer = new $$asap$$BrowserMutationObserver($$asap$$flush); |
|||
var node = document.createTextNode(''); |
|||
observer.observe(node, { characterData: true }); |
|||
|
|||
return function() { |
|||
node.data = (iterations = ++iterations % 2); |
|||
}; |
|||
} |
|||
|
|||
// web worker
|
|||
function $$asap$$useMessageChannel() { |
|||
var channel = new MessageChannel(); |
|||
channel.port1.onmessage = $$asap$$flush; |
|||
return function () { |
|||
channel.port2.postMessage(0); |
|||
}; |
|||
} |
|||
|
|||
function $$asap$$useSetTimeout() { |
|||
return function() { |
|||
setTimeout($$asap$$flush, 1); |
|||
}; |
|||
} |
|||
|
|||
var $$asap$$queue = new Array(1000); |
|||
|
|||
function $$asap$$flush() { |
|||
for (var i = 0; i < $$asap$$len; i+=2) { |
|||
var callback = $$asap$$queue[i]; |
|||
var arg = $$asap$$queue[i+1]; |
|||
|
|||
callback(arg); |
|||
|
|||
$$asap$$queue[i] = undefined; |
|||
$$asap$$queue[i+1] = undefined; |
|||
} |
|||
|
|||
$$asap$$len = 0; |
|||
} |
|||
|
|||
var $$asap$$scheduleFlush; |
|||
|
|||
// Decide what async method to use to triggering processing of queued callbacks:
|
|||
if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') { |
|||
$$asap$$scheduleFlush = $$asap$$useNextTick(); |
|||
} else if ($$asap$$BrowserMutationObserver) { |
|||
$$asap$$scheduleFlush = $$asap$$useMutationObserver(); |
|||
} else if ($$asap$$isWorker) { |
|||
$$asap$$scheduleFlush = $$asap$$useMessageChannel(); |
|||
} else { |
|||
$$asap$$scheduleFlush = $$asap$$useSetTimeout(); |
|||
} |
|||
|
|||
function $$$internal$$noop() {} |
|||
var $$$internal$$PENDING = void 0; |
|||
var $$$internal$$FULFILLED = 1; |
|||
var $$$internal$$REJECTED = 2; |
|||
var $$$internal$$GET_THEN_ERROR = new $$$internal$$ErrorObject(); |
|||
|
|||
function $$$internal$$selfFullfillment() { |
|||
return new TypeError("You cannot resolve a promise with itself"); |
|||
} |
|||
|
|||
function $$$internal$$cannotReturnOwn() { |
|||
return new TypeError('A promises callback cannot return that same promise.') |
|||
} |
|||
|
|||
function $$$internal$$getThen(promise) { |
|||
try { |
|||
return promise.then; |
|||
} catch(error) { |
|||
$$$internal$$GET_THEN_ERROR.error = error; |
|||
return $$$internal$$GET_THEN_ERROR; |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) { |
|||
try { |
|||
then.call(value, fulfillmentHandler, rejectionHandler); |
|||
} catch(e) { |
|||
return e; |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$handleForeignThenable(promise, thenable, then) { |
|||
$$asap$$default(function(promise) { |
|||
var sealed = false; |
|||
var error = $$$internal$$tryThen(then, thenable, function(value) { |
|||
if (sealed) { return; } |
|||
sealed = true; |
|||
if (thenable !== value) { |
|||
$$$internal$$resolve(promise, value); |
|||
} else { |
|||
$$$internal$$fulfill(promise, value); |
|||
} |
|||
}, function(reason) { |
|||
if (sealed) { return; } |
|||
sealed = true; |
|||
|
|||
$$$internal$$reject(promise, reason); |
|||
}, 'Settle: ' + (promise._label || ' unknown promise')); |
|||
|
|||
if (!sealed && error) { |
|||
sealed = true; |
|||
$$$internal$$reject(promise, error); |
|||
} |
|||
}, promise); |
|||
} |
|||
|
|||
function $$$internal$$handleOwnThenable(promise, thenable) { |
|||
if (thenable._state === $$$internal$$FULFILLED) { |
|||
$$$internal$$fulfill(promise, thenable._result); |
|||
} else if (promise._state === $$$internal$$REJECTED) { |
|||
$$$internal$$reject(promise, thenable._result); |
|||
} else { |
|||
$$$internal$$subscribe(thenable, undefined, function(value) { |
|||
$$$internal$$resolve(promise, value); |
|||
}, function(reason) { |
|||
$$$internal$$reject(promise, reason); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$handleMaybeThenable(promise, maybeThenable) { |
|||
if (maybeThenable.constructor === promise.constructor) { |
|||
$$$internal$$handleOwnThenable(promise, maybeThenable); |
|||
} else { |
|||
var then = $$$internal$$getThen(maybeThenable); |
|||
|
|||
if (then === $$$internal$$GET_THEN_ERROR) { |
|||
$$$internal$$reject(promise, $$$internal$$GET_THEN_ERROR.error); |
|||
} else if (then === undefined) { |
|||
$$$internal$$fulfill(promise, maybeThenable); |
|||
} else if ($$utils$$isFunction(then)) { |
|||
$$$internal$$handleForeignThenable(promise, maybeThenable, then); |
|||
} else { |
|||
$$$internal$$fulfill(promise, maybeThenable); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$resolve(promise, value) { |
|||
if (promise === value) { |
|||
$$$internal$$reject(promise, $$$internal$$selfFullfillment()); |
|||
} else if ($$utils$$objectOrFunction(value)) { |
|||
$$$internal$$handleMaybeThenable(promise, value); |
|||
} else { |
|||
$$$internal$$fulfill(promise, value); |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$publishRejection(promise) { |
|||
if (promise._onerror) { |
|||
promise._onerror(promise._result); |
|||
} |
|||
|
|||
$$$internal$$publish(promise); |
|||
} |
|||
|
|||
function $$$internal$$fulfill(promise, value) { |
|||
if (promise._state !== $$$internal$$PENDING) { return; } |
|||
|
|||
promise._result = value; |
|||
promise._state = $$$internal$$FULFILLED; |
|||
|
|||
if (promise._subscribers.length === 0) { |
|||
} else { |
|||
$$asap$$default($$$internal$$publish, promise); |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$reject(promise, reason) { |
|||
if (promise._state !== $$$internal$$PENDING) { return; } |
|||
promise._state = $$$internal$$REJECTED; |
|||
promise._result = reason; |
|||
|
|||
$$asap$$default($$$internal$$publishRejection, promise); |
|||
} |
|||
|
|||
function $$$internal$$subscribe(parent, child, onFulfillment, onRejection) { |
|||
var subscribers = parent._subscribers; |
|||
var length = subscribers.length; |
|||
|
|||
parent._onerror = null; |
|||
|
|||
subscribers[length] = child; |
|||
subscribers[length + $$$internal$$FULFILLED] = onFulfillment; |
|||
subscribers[length + $$$internal$$REJECTED] = onRejection; |
|||
|
|||
if (length === 0 && parent._state) { |
|||
$$asap$$default($$$internal$$publish, parent); |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$publish(promise) { |
|||
var subscribers = promise._subscribers; |
|||
var settled = promise._state; |
|||
|
|||
if (subscribers.length === 0) { return; } |
|||
|
|||
var child, callback, detail = promise._result; |
|||
|
|||
for (var i = 0; i < subscribers.length; i += 3) { |
|||
child = subscribers[i]; |
|||
callback = subscribers[i + settled]; |
|||
|
|||
if (child) { |
|||
$$$internal$$invokeCallback(settled, child, callback, detail); |
|||
} else { |
|||
callback(detail); |
|||
} |
|||
} |
|||
|
|||
promise._subscribers.length = 0; |
|||
} |
|||
|
|||
function $$$internal$$ErrorObject() { |
|||
this.error = null; |
|||
} |
|||
|
|||
var $$$internal$$TRY_CATCH_ERROR = new $$$internal$$ErrorObject(); |
|||
|
|||
function $$$internal$$tryCatch(callback, detail) { |
|||
try { |
|||
return callback(detail); |
|||
} catch(e) { |
|||
$$$internal$$TRY_CATCH_ERROR.error = e; |
|||
return $$$internal$$TRY_CATCH_ERROR; |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$invokeCallback(settled, promise, callback, detail) { |
|||
var hasCallback = $$utils$$isFunction(callback), |
|||
value, error, succeeded, failed; |
|||
|
|||
if (hasCallback) { |
|||
value = $$$internal$$tryCatch(callback, detail); |
|||
|
|||
if (value === $$$internal$$TRY_CATCH_ERROR) { |
|||
failed = true; |
|||
error = value.error; |
|||
value = null; |
|||
} else { |
|||
succeeded = true; |
|||
} |
|||
|
|||
if (promise === value) { |
|||
$$$internal$$reject(promise, $$$internal$$cannotReturnOwn()); |
|||
return; |
|||
} |
|||
|
|||
} else { |
|||
value = detail; |
|||
succeeded = true; |
|||
} |
|||
|
|||
if (promise._state !== $$$internal$$PENDING) { |
|||
// noop
|
|||
} else if (hasCallback && succeeded) { |
|||
$$$internal$$resolve(promise, value); |
|||
} else if (failed) { |
|||
$$$internal$$reject(promise, error); |
|||
} else if (settled === $$$internal$$FULFILLED) { |
|||
$$$internal$$fulfill(promise, value); |
|||
} else if (settled === $$$internal$$REJECTED) { |
|||
$$$internal$$reject(promise, value); |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$initializePromise(promise, resolver) { |
|||
try { |
|||
resolver(function resolvePromise(value){ |
|||
$$$internal$$resolve(promise, value); |
|||
}, function rejectPromise(reason) { |
|||
$$$internal$$reject(promise, reason); |
|||
}); |
|||
} catch(e) { |
|||
$$$internal$$reject(promise, e); |
|||
} |
|||
} |
|||
|
|||
function $$$enumerator$$makeSettledResult(state, position, value) { |
|||
if (state === $$$internal$$FULFILLED) { |
|||
return { |
|||
state: 'fulfilled', |
|||
value: value |
|||
}; |
|||
} else { |
|||
return { |
|||
state: 'rejected', |
|||
reason: value |
|||
}; |
|||
} |
|||
} |
|||
|
|||
function $$$enumerator$$Enumerator(Constructor, input, abortOnReject, label) { |
|||
this._instanceConstructor = Constructor; |
|||
this.promise = new Constructor($$$internal$$noop, label); |
|||
this._abortOnReject = abortOnReject; |
|||
|
|||
if (this._validateInput(input)) { |
|||
this._input = input; |
|||
this.length = input.length; |
|||
this._remaining = input.length; |
|||
|
|||
this._init(); |
|||
|
|||
if (this.length === 0) { |
|||
$$$internal$$fulfill(this.promise, this._result); |
|||
} else { |
|||
this.length = this.length || 0; |
|||
this._enumerate(); |
|||
if (this._remaining === 0) { |
|||
$$$internal$$fulfill(this.promise, this._result); |
|||
} |
|||
} |
|||
} else { |
|||
$$$internal$$reject(this.promise, this._validationError()); |
|||
} |
|||
} |
|||
|
|||
$$$enumerator$$Enumerator.prototype._validateInput = function(input) { |
|||
return $$utils$$isArray(input); |
|||
}; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._validationError = function() { |
|||
return new Error('Array Methods must be provided an Array'); |
|||
}; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._init = function() { |
|||
this._result = new Array(this.length); |
|||
}; |
|||
|
|||
var $$$enumerator$$default = $$$enumerator$$Enumerator; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._enumerate = function() { |
|||
var length = this.length; |
|||
var promise = this.promise; |
|||
var input = this._input; |
|||
|
|||
for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) { |
|||
this._eachEntry(input[i], i); |
|||
} |
|||
}; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) { |
|||
var c = this._instanceConstructor; |
|||
if ($$utils$$isMaybeThenable(entry)) { |
|||
if (entry.constructor === c && entry._state !== $$$internal$$PENDING) { |
|||
entry._onerror = null; |
|||
this._settledAt(entry._state, i, entry._result); |
|||
} else { |
|||
this._willSettleAt(c.resolve(entry), i); |
|||
} |
|||
} else { |
|||
this._remaining--; |
|||
this._result[i] = this._makeResult($$$internal$$FULFILLED, i, entry); |
|||
} |
|||
}; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) { |
|||
var promise = this.promise; |
|||
|
|||
if (promise._state === $$$internal$$PENDING) { |
|||
this._remaining--; |
|||
|
|||
if (this._abortOnReject && state === $$$internal$$REJECTED) { |
|||
$$$internal$$reject(promise, value); |
|||
} else { |
|||
this._result[i] = this._makeResult(state, i, value); |
|||
} |
|||
} |
|||
|
|||
if (this._remaining === 0) { |
|||
$$$internal$$fulfill(promise, this._result); |
|||
} |
|||
}; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._makeResult = function(state, i, value) { |
|||
return value; |
|||
}; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) { |
|||
var enumerator = this; |
|||
|
|||
$$$internal$$subscribe(promise, undefined, function(value) { |
|||
enumerator._settledAt($$$internal$$FULFILLED, i, value); |
|||
}, function(reason) { |
|||
enumerator._settledAt($$$internal$$REJECTED, i, reason); |
|||
}); |
|||
}; |
|||
|
|||
var $$promise$all$$default = function all(entries, label) { |
|||
return new $$$enumerator$$default(this, entries, true /* abort on reject */, label).promise; |
|||
}; |
|||
|
|||
var $$promise$race$$default = function race(entries, label) { |
|||
/*jshint validthis:true */ |
|||
var Constructor = this; |
|||
|
|||
var promise = new Constructor($$$internal$$noop, label); |
|||
|
|||
if (!$$utils$$isArray(entries)) { |
|||
$$$internal$$reject(promise, new TypeError('You must pass an array to race.')); |
|||
return promise; |
|||
} |
|||
|
|||
var length = entries.length; |
|||
|
|||
function onFulfillment(value) { |
|||
$$$internal$$resolve(promise, value); |
|||
} |
|||
|
|||
function onRejection(reason) { |
|||
$$$internal$$reject(promise, reason); |
|||
} |
|||
|
|||
for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) { |
|||
$$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection); |
|||
} |
|||
|
|||
return promise; |
|||
}; |
|||
|
|||
var $$promise$resolve$$default = function resolve(object, label) { |
|||
/*jshint validthis:true */ |
|||
var Constructor = this; |
|||
|
|||
if (object && typeof object === 'object' && object.constructor === Constructor) { |
|||
return object; |
|||
} |
|||
|
|||
var promise = new Constructor($$$internal$$noop, label); |
|||
$$$internal$$resolve(promise, object); |
|||
return promise; |
|||
}; |
|||
|
|||
var $$promise$reject$$default = function reject(reason, label) { |
|||
/*jshint validthis:true */ |
|||
var Constructor = this; |
|||
var promise = new Constructor($$$internal$$noop, label); |
|||
$$$internal$$reject(promise, reason); |
|||
return promise; |
|||
}; |
|||
|
|||
var $$es6$promise$promise$$counter = 0; |
|||
|
|||
function $$es6$promise$promise$$needsResolver() { |
|||
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); |
|||
} |
|||
|
|||
function $$es6$promise$promise$$needsNew() { |
|||
throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); |
|||
} |
|||
|
|||
var $$es6$promise$promise$$default = $$es6$promise$promise$$Promise; |
|||
|
|||
/** |
|||
Promise objects represent the eventual result of an asynchronous operation. The |
|||
primary way of interacting with a promise is through its `then` method, which |
|||
registers callbacks to receive either a promise’s eventual value or the reason |
|||
why the promise cannot be fulfilled. |
|||
|
|||
Terminology |
|||
----------- |
|||
|
|||
- `promise` is an object or function with a `then` method whose behavior conforms to this specification. |
|||
- `thenable` is an object or function that defines a `then` method. |
|||
- `value` is any legal JavaScript value (including undefined, a thenable, or a promise). |
|||
- `exception` is a value that is thrown using the throw statement. |
|||
- `reason` is a value that indicates why a promise was rejected. |
|||
- `settled` the final resting state of a promise, fulfilled or rejected. |
|||
|
|||
A promise can be in one of three states: pending, fulfilled, or rejected. |
|||
|
|||
Promises that are fulfilled have a fulfillment value and are in the fulfilled |
|||
state. Promises that are rejected have a rejection reason and are in the |
|||
rejected state. A fulfillment value is never a thenable. |
|||
|
|||
Promises can also be said to *resolve* a value. If this value is also a |
|||
promise, then the original promise's settled state will match the value's |
|||
settled state. So a promise that *resolves* a promise that rejects will |
|||
itself reject, and a promise that *resolves* a promise that fulfills will |
|||
itself fulfill. |
|||
|
|||
|
|||
Basic Usage: |
|||
------------ |
|||
|
|||
```js
|
|||
var promise = new Promise(function(resolve, reject) { |
|||
// on success
|
|||
resolve(value); |
|||
|
|||
// on failure
|
|||
reject(reason); |
|||
}); |
|||
|
|||
promise.then(function(value) { |
|||
// on fulfillment
|
|||
}, function(reason) { |
|||
// on rejection
|
|||
}); |
|||
``` |
|||
|
|||
Advanced Usage: |
|||
--------------- |
|||
|
|||
Promises shine when abstracting away asynchronous interactions such as |
|||
`XMLHttpRequest`s. |
|||
|
|||
```js
|
|||
function getJSON(url) { |
|||
return new Promise(function(resolve, reject){ |
|||
var xhr = new XMLHttpRequest(); |
|||
|
|||
xhr.open('GET', url); |
|||
xhr.onreadystatechange = handler; |
|||
xhr.responseType = 'json'; |
|||
xhr.setRequestHeader('Accept', 'application/json'); |
|||
xhr.send(); |
|||
|
|||
function handler() { |
|||
if (this.readyState === this.DONE) { |
|||
if (this.status === 200) { |
|||
resolve(this.response); |
|||
} else { |
|||
reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); |
|||
} |
|||
} |
|||
}; |
|||
}); |
|||
} |
|||
|
|||
getJSON('/posts.json').then(function(json) { |
|||
// on fulfillment
|
|||
}, function(reason) { |
|||
// on rejection
|
|||
}); |
|||
``` |
|||
|
|||
Unlike callbacks, promises are great composable primitives. |
|||
|
|||
```js
|
|||
Promise.all([ |
|||
getJSON('/posts'), |
|||
getJSON('/comments') |
|||
]).then(function(values){ |
|||
values[0] // => postsJSON
|
|||
values[1] // => commentsJSON
|
|||
|
|||
return values; |
|||
}); |
|||
``` |
|||
|
|||
@class Promise |
|||
@param {function} resolver |
|||
@param {String} label optional string for labeling the promise. |
|||
Useful for tooling. |
|||
@constructor |
|||
*/ |
|||
function $$es6$promise$promise$$Promise(resolver, label) { |
|||
this._id = $$es6$promise$promise$$counter++; |
|||
this._label = label; |
|||
this._state = undefined; |
|||
this._result = undefined; |
|||
this._subscribers = []; |
|||
|
|||
if ($$$internal$$noop !== resolver) { |
|||
if (!$$utils$$isFunction(resolver)) { |
|||
$$es6$promise$promise$$needsResolver(); |
|||
} |
|||
|
|||
if (!(this instanceof $$es6$promise$promise$$Promise)) { |
|||
$$es6$promise$promise$$needsNew(); |
|||
} |
|||
|
|||
$$$internal$$initializePromise(this, resolver); |
|||
} |
|||
} |
|||
|
|||
$$es6$promise$promise$$Promise.all = $$promise$all$$default; |
|||
$$es6$promise$promise$$Promise.race = $$promise$race$$default; |
|||
$$es6$promise$promise$$Promise.resolve = $$promise$resolve$$default; |
|||
$$es6$promise$promise$$Promise.reject = $$promise$reject$$default; |
|||
|
|||
$$es6$promise$promise$$Promise.prototype = { |
|||
constructor: $$es6$promise$promise$$Promise, |
|||
|
|||
/** |
|||
The primary way of interacting with a promise is through its `then` method, |
|||
which registers callbacks to receive either a promise's eventual value or the |
|||
reason why the promise cannot be fulfilled. |
|||
|
|||
```js
|
|||
findUser().then(function(user){ |
|||
// user is available
|
|||
}, function(reason){ |
|||
// user is unavailable, and you are given the reason why
|
|||
}); |
|||
``` |
|||
|
|||
Chaining |
|||
-------- |
|||
|
|||
The return value of `then` is itself a promise. This second, 'downstream' |
|||
promise is resolved with the return value of the first promise's fulfillment |
|||
or rejection handler, or rejected if the handler throws an exception. |
|||
|
|||
```js
|
|||
findUser().then(function (user) { |
|||
return user.name; |
|||
}, function (reason) { |
|||
return 'default name'; |
|||
}).then(function (userName) { |
|||
// If `findUser` fulfilled, `userName` will be the user's name, otherwise it
|
|||
// will be `'default name'`
|
|||
}); |
|||
|
|||
findUser().then(function (user) { |
|||
throw new Error('Found user, but still unhappy'); |
|||
}, function (reason) { |
|||
throw new Error('`findUser` rejected and we're unhappy'); |
|||
}).then(function (value) { |
|||
// never reached
|
|||
}, function (reason) { |
|||
// if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
|
|||
// If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
|
|||
}); |
|||
``` |
|||
If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. |
|||
|
|||
```js
|
|||
findUser().then(function (user) { |
|||
throw new PedagogicalException('Upstream error'); |
|||
}).then(function (value) { |
|||
// never reached
|
|||
}).then(function (value) { |
|||
// never reached
|
|||
}, function (reason) { |
|||
// The `PedgagocialException` is propagated all the way down to here
|
|||
}); |
|||
``` |
|||
|
|||
Assimilation |
|||
------------ |
|||
|
|||
Sometimes the value you want to propagate to a downstream promise can only be |
|||
retrieved asynchronously. This can be achieved by returning a promise in the |
|||
fulfillment or rejection handler. The downstream promise will then be pending |
|||
until the returned promise is settled. This is called *assimilation*. |
|||
|
|||
```js
|
|||
findUser().then(function (user) { |
|||
return findCommentsByAuthor(user); |
|||
}).then(function (comments) { |
|||
// The user's comments are now available
|
|||
}); |
|||
``` |
|||
|
|||
If the assimliated promise rejects, then the downstream promise will also reject. |
|||
|
|||
```js
|
|||
findUser().then(function (user) { |
|||
return findCommentsByAuthor(user); |
|||
}).then(function (comments) { |
|||
// If `findCommentsByAuthor` fulfills, we'll have the value here
|
|||
}, function (reason) { |
|||
// If `findCommentsByAuthor` rejects, we'll have the reason here
|
|||
}); |
|||
``` |
|||
|
|||
Simple Example |
|||
-------------- |
|||
|
|||
Synchronous Example |
|||
|
|||
```javascript
|
|||
var result; |
|||
|
|||
try { |
|||
result = findResult(); |
|||
// success
|
|||
} catch(reason) { |
|||
// failure
|
|||
} |
|||
``` |
|||
|
|||
Errback Example |
|||
|
|||
```js
|
|||
findResult(function(result, err){ |
|||
if (err) { |
|||
// failure
|
|||
} else { |
|||
// success
|
|||
} |
|||
}); |
|||
``` |
|||
|
|||
Promise Example; |
|||
|
|||
```javascript
|
|||
findResult().then(function(result){ |
|||
// success
|
|||
}, function(reason){ |
|||
// failure
|
|||
}); |
|||
``` |
|||
|
|||
Advanced Example |
|||
-------------- |
|||
|
|||
Synchronous Example |
|||
|
|||
```javascript
|
|||
var author, books; |
|||
|
|||
try { |
|||
author = findAuthor(); |
|||
books = findBooksByAuthor(author); |
|||
// success
|
|||
} catch(reason) { |
|||
// failure
|
|||
} |
|||
``` |
|||
|
|||
Errback Example |
|||
|
|||
```js
|
|||
|
|||
function foundBooks(books) { |
|||
|
|||
} |
|||
|
|||
function failure(reason) { |
|||
|
|||
} |
|||
|
|||
findAuthor(function(author, err){ |
|||
if (err) { |
|||
failure(err); |
|||
// failure
|
|||
} else { |
|||
try { |
|||
findBoooksByAuthor(author, function(books, err) { |
|||
if (err) { |
|||
failure(err); |
|||
} else { |
|||
try { |
|||
foundBooks(books); |
|||
} catch(reason) { |
|||
failure(reason); |
|||
} |
|||
} |
|||
}); |
|||
} catch(error) { |
|||
failure(err); |
|||
} |
|||
// success
|
|||
} |
|||
}); |
|||
``` |
|||
|
|||
Promise Example; |
|||
|
|||
```javascript
|
|||
findAuthor(). |
|||
then(findBooksByAuthor). |
|||
then(function(books){ |
|||
// found books
|
|||
}).catch(function(reason){ |
|||
// something went wrong
|
|||
}); |
|||
``` |
|||
|
|||
@method then |
|||
@param {Function} onFulfilled |
|||
@param {Function} onRejected |
|||
@param {String} label optional string for labeling the promise. |
|||
Useful for tooling. |
|||
@return {Promise} |
|||
*/ |
|||
then: function(onFulfillment, onRejection, label) { |
|||
var parent = this; |
|||
var state = parent._state; |
|||
|
|||
if (state === $$$internal$$FULFILLED && !onFulfillment || state === $$$internal$$REJECTED && !onRejection) { |
|||
return this; |
|||
} |
|||
|
|||
parent._onerror = null; |
|||
|
|||
var child = new this.constructor($$$internal$$noop, label); |
|||
var result = parent._result; |
|||
|
|||
if (state) { |
|||
var callback = arguments[state - 1]; |
|||
$$asap$$default(function(){ |
|||
$$$internal$$invokeCallback(state, child, callback, result); |
|||
}); |
|||
} else { |
|||
$$$internal$$subscribe(parent, child, onFulfillment, onRejection); |
|||
} |
|||
|
|||
return child; |
|||
}, |
|||
|
|||
/** |
|||
`catch` is simply sugar for `then(undefined, onRejection)` which makes it the same |
|||
as the catch block of a try/catch statement. |
|||
|
|||
```js
|
|||
function findAuthor(){ |
|||
throw new Error('couldn't find that author'); |
|||
} |
|||
|
|||
// synchronous
|
|||
try { |
|||
findAuthor(); |
|||
} catch(reason) { |
|||
// something went wrong
|
|||
} |
|||
|
|||
// async with promises
|
|||
findAuthor().catch(function(reason){ |
|||
// something went wrong
|
|||
}); |
|||
``` |
|||
|
|||
@method catch |
|||
@param {Function} onRejection |
|||
@param {String} label optional string for labeling the promise. |
|||
Useful for tooling. |
|||
@return {Promise} |
|||
*/ |
|||
'catch': function(onRejection, label) { |
|||
return this.then(null, onRejection, label); |
|||
} |
|||
}; |
|||
|
|||
var $$es6$promise$polyfill$$default = function polyfill() { |
|||
var local; |
|||
|
|||
if (typeof global !== 'undefined') { |
|||
local = global; |
|||
} else if (typeof window !== 'undefined' && window.document) { |
|||
local = window; |
|||
} else { |
|||
local = self; |
|||
} |
|||
|
|||
var es6PromiseSupport = |
|||
"Promise" in local && |
|||
// Some of these methods are missing from
|
|||
// Firefox/Chrome experimental implementations
|
|||
"resolve" in local.Promise && |
|||
"reject" in local.Promise && |
|||
"all" in local.Promise && |
|||
"race" in local.Promise && |
|||
// Older version of the spec had a resolver object
|
|||
// as the arg rather than a function
|
|||
(function() { |
|||
var resolve; |
|||
new local.Promise(function(r) { resolve = r; }); |
|||
return $$utils$$isFunction(resolve); |
|||
}()); |
|||
|
|||
if (!es6PromiseSupport) { |
|||
local.Promise = $$es6$promise$promise$$default; |
|||
} |
|||
}; |
|||
|
|||
var es6$promise$umd$$ES6Promise = { |
|||
Promise: $$es6$promise$promise$$default, |
|||
polyfill: $$es6$promise$polyfill$$default |
|||
}; |
|||
|
|||
/* global define:true module:true window: true */ |
|||
if (typeof define === 'function' && define['amd']) { |
|||
define(function() { return es6$promise$umd$$ES6Promise; }); |
|||
} else if (typeof module !== 'undefined' && module['exports']) { |
|||
module['exports'] = es6$promise$umd$$ES6Promise; |
|||
} else if (typeof this !== 'undefined') { |
|||
this['ES6Promise'] = es6$promise$umd$$ES6Promise; |
|||
} |
|||
}).call(this); |
File diff suppressed because it is too large
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,76 @@ |
|||
<!doctype> |
|||
<html> |
|||
|
|||
<head> |
|||
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script> |
|||
<script type="text/javascript" src="../dist/ethereum.js"></script> |
|||
<script type="text/javascript"> |
|||
|
|||
var web3 = require('web3'); |
|||
web3.setProvider(new web3.providers.HttpSyncProvider()); |
|||
|
|||
// solidity source code |
|||
var source = "" + |
|||
"contract test {\n" + |
|||
" function multiply(uint[] a) returns(uint d) {\n" + |
|||
" return a[0] + a[1];\n" + |
|||
" }\n" + |
|||
"}\n"; |
|||
|
|||
// contract description, this will be autogenerated somehow |
|||
var desc = [{ |
|||
"name": "multiply(uint256[])", |
|||
"type": "function", |
|||
"inputs": [ |
|||
{ |
|||
"name": "a", |
|||
"type": "uint256[]" |
|||
} |
|||
], |
|||
"outputs": [ |
|||
{ |
|||
"name": "d", |
|||
"type": "uint256" |
|||
} |
|||
] |
|||
}]; |
|||
|
|||
var contract; |
|||
|
|||
function createExampleContract() { |
|||
// hide create button |
|||
document.getElementById('create').style.visibility = 'hidden'; |
|||
document.getElementById('source').innerText = source; |
|||
|
|||
// create contract |
|||
var address = web3.eth.transact({code: web3.eth.solidity(source)}); |
|||
contract = web3.eth.contract(address, desc); |
|||
document.getElementById('call').style.visibility = 'visible'; |
|||
} |
|||
|
|||
function callExampleContract() { |
|||
// this should be generated by ethereum |
|||
var param = parseInt(document.getElementById('value').value); |
|||
var param2 = parseInt(document.getElementById('value2').value); |
|||
|
|||
// call the contract |
|||
var res = contract.call().multiply([param, param2]); |
|||
document.getElementById('result').innerText = res.toString(10); |
|||
} |
|||
|
|||
</script> |
|||
</head> |
|||
<body> |
|||
<h1>contract</h1> |
|||
<div id="source"></div> |
|||
<div id='create'> |
|||
<button type="button" onClick="createExampleContract();">create example contract</button> |
|||
</div> |
|||
<div id='call' style='visibility: hidden;'> |
|||
<input type="number" id="value" onkeyup='callExampleContract()'></input> |
|||
<input type="number" id="value2" onkeyup='callExampleContract()'></input> |
|||
</div> |
|||
<div id="result"></div> |
|||
</body> |
|||
</html> |
|||
|
@ -0,0 +1,120 @@ |
|||
<!doctype> |
|||
<html> |
|||
<head> |
|||
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script> |
|||
<script type="text/javascript" src="../dist/ethereum.js"></script> |
|||
<script type="text/javascript"> |
|||
var web3 = require('web3'); |
|||
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8080')); |
|||
|
|||
var desc = [{ |
|||
"type":"event", |
|||
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}], |
|||
"name":"Event" |
|||
}, { |
|||
"type":"event", |
|||
"inputs": [{"name":"a","type":"uint256","indexed":true},{"name":"b","type":"hash256","indexed":false}], |
|||
"name":"Event2" |
|||
}, { |
|||
"type":"function", |
|||
"inputs": [{"name":"a","type":"uint256"}], |
|||
"name":"foo", |
|||
"outputs": [] |
|||
}]; |
|||
|
|||
var address = '0x01'; |
|||
|
|||
var contract = web3.eth.contract(address, desc); |
|||
|
|||
function test1() { |
|||
// "{"topic":["0x83c9849c","0xc4d76332"],"address":"0x01"}" |
|||
web3.eth.watch(contract).changed(function (res) { |
|||
|
|||
}); |
|||
}; |
|||
|
|||
function test2() { |
|||
// "{"topic":["0x83c9849c"],"address":"0x01"}" |
|||
web3.eth.watch(contract.Event).changed(function (res) { |
|||
|
|||
}); |
|||
}; |
|||
|
|||
function test3() { |
|||
// "{"topic":["0x83c9849c"],"address":"0x01"}" |
|||
contract.Event().changed(function (res) { |
|||
|
|||
}); |
|||
}; |
|||
|
|||
function test4() { |
|||
// "{"topic":["0x83c9849c","0000000000000000000000000000000000000000000000000000000000000045"],"address":"0x01"}" |
|||
contract.Event({a: 69}).changed(function (res) { |
|||
|
|||
}); |
|||
}; |
|||
|
|||
function test5() { |
|||
// "{"topic":["0x83c9849c",["0000000000000000000000000000000000000000000000000000000000000045","000000000000000000000000000000000000000000000000000000000000002a"]],"address":"0x01"}" |
|||
contract.Event({a: [69, 42]}).changed(function (res) { |
|||
|
|||
}); |
|||
}; |
|||
|
|||
function test6() { |
|||
// "{"topic":["0x83c9849c","000000000000000000000000000000000000000000000000000000000000001e"],"max":100,"address":"0x01"}" |
|||
contract.Event({a: 30}, {max: 100}).changed(function (res) { |
|||
|
|||
}); |
|||
}; |
|||
|
|||
function test7() { |
|||
// "{"topic":["0x83c9849c","000000000000000000000000000000000000000000000000000000000000001e"],"address":"0x01"}" |
|||
web3.eth.watch(contract.Event, {a: 30}).changed(function (res) { |
|||
|
|||
}); |
|||
}; |
|||
|
|||
function test8() { |
|||
// "{"topic":["0x83c9849c","000000000000000000000000000000000000000000000000000000000000001e"],"max":100,"address":"0x01"}" |
|||
web3.eth.watch(contract.Event, {a: 30}, {max: 100}).changed(function (res) { |
|||
|
|||
}); |
|||
}; |
|||
|
|||
// not valid |
|||
// function testX() { |
|||
// web3.eth.watch([contract.Event, contract.Event2]).changed(function (res) { |
|||
// }); |
|||
// }; |
|||
|
|||
</script> |
|||
</head> |
|||
|
|||
<body> |
|||
<div> |
|||
<button type="button" onClick="test1();">test1</button> |
|||
</div> |
|||
<div> |
|||
<button type="button" onClick="test2();">test2</button> |
|||
</div> |
|||
<div> |
|||
<button type="button" onClick="test3();">test3</button> |
|||
</div> |
|||
<div> |
|||
<button type="button" onClick="test4();">test4</button> |
|||
</div> |
|||
<div> |
|||
<button type="button" onClick="test5();">test5</button> |
|||
</div> |
|||
<div> |
|||
<button type="button" onClick="test6();">test6</button> |
|||
</div> |
|||
<div> |
|||
<button type="button" onClick="test7();">test7</button> |
|||
</div> |
|||
<div> |
|||
<button type="button" onClick="test8();">test8</button> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,66 @@ |
|||
<!doctype> |
|||
<html> |
|||
<head> |
|||
<script type="text/javascript" src="js/bignumber.js/bignumber.min.js"></script> |
|||
<script type="text/javascript" src="../dist/ethereum.js"></script> |
|||
<script type="text/javascript"> |
|||
var web3 = require('web3'); |
|||
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8080')); |
|||
|
|||
var source = "" + |
|||
"contract Contract { " + |
|||
" event Incremented(bool indexed odd, uint x); " + |
|||
" function Contract() { " + |
|||
" x = 69; " + |
|||
" } " + |
|||
" function inc() { " + |
|||
" ++x; " + |
|||
" Incremented(x % 2 == 1, x); " + |
|||
" } " + |
|||
" uint x; " + |
|||
"}"; |
|||
|
|||
var desc = [{ |
|||
"type":"event", |
|||
"name":"Incremented", |
|||
"inputs": [{"name":"odd","type":"bool","indexed":true},{"name":"x","type":"uint","indexed":false}], |
|||
}, { |
|||
"type":"function", |
|||
"name":"inc", |
|||
"inputs": [], |
|||
"outputs": [] |
|||
}]; |
|||
|
|||
var address; |
|||
var contract; |
|||
|
|||
var update = function (x) { |
|||
document.getElementById('result').innerText = JSON.stringify(x); |
|||
}; |
|||
|
|||
var createContract = function () { |
|||
address = web3.eth.transact({code: web3.eth.solidity(source)}); |
|||
contract = web3.eth.contract(address, desc); |
|||
contract.Incremented({odd: true}).changed(update); |
|||
|
|||
}; |
|||
|
|||
var callContract = function () { |
|||
contract.call().inc(); |
|||
}; |
|||
|
|||
|
|||
</script> |
|||
</head> |
|||
|
|||
<body> |
|||
<div> |
|||
<button type="button" onClick="createContract();">create contract</button> |
|||
</div> |
|||
<div> |
|||
<button type="button" onClick="callContract();">test1</button> |
|||
</div> |
|||
<div id="result"> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -1,16 +1,12 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
require('es6-promise').polyfill(); |
|||
|
|||
var web3 = require("../index.js"); |
|||
|
|||
web3.setProvider(new web3.providers.HttpRpcProvider('http://localhost:8080')); |
|||
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8080')); |
|||
|
|||
var coinbase = web3.eth.coinbase; |
|||
console.log(coinbase); |
|||
|
|||
var balance = web3.eth.balanceAt(coinbase); |
|||
console.log(balance); |
|||
|
|||
web3.eth.coinbase.then(function(result){ |
|||
console.log(result); |
|||
return web3.eth.balanceAt(result); |
|||
}).then(function(balance){ |
|||
console.log(web3.toDecimal(balance)); |
|||
}).catch(function(err){ |
|||
console.log(err); |
|||
}); |
@ -1,8 +1,11 @@ |
|||
var web3 = require('./lib/web3'); |
|||
web3.providers.WebSocketProvider = require('./lib/websocket'); |
|||
web3.providers.HttpRpcProvider = require('./lib/httprpc'); |
|||
web3.providers.QtProvider = require('./lib/qt'); |
|||
web3.providers.AutoProvider = require('./lib/autoprovider'); |
|||
web3.contract = require('./lib/contract'); |
|||
var ProviderManager = require('./lib/providermanager'); |
|||
web3.provider = new ProviderManager(); |
|||
web3.filter = require('./lib/filter'); |
|||
web3.providers.HttpSyncProvider = require('./lib/httpsync'); |
|||
web3.providers.QtSyncProvider = require('./lib/qtsync'); |
|||
web3.eth.contract = require('./lib/contract'); |
|||
web3.abi = require('./lib/abi'); |
|||
|
|||
|
|||
module.exports = web3; |
|||
|
@ -1,102 +0,0 @@ |
|||
/* |
|||
This file is part of ethereum.js. |
|||
|
|||
ethereum.js is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Lesser General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
ethereum.js is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public License |
|||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file autoprovider.js |
|||
* @authors: |
|||
* Marek Kotewicz <marek@ethdev.com> |
|||
* Marian Oancea <marian@ethdev.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
/* |
|||
* @brief if qt object is available, uses QtProvider, |
|||
* if not tries to connect over websockets |
|||
* if it fails, it uses HttpRpcProvider |
|||
*/ |
|||
|
|||
// TODO: is these line is supposed to be here?
|
|||
if (process.env.NODE_ENV !== 'build') { |
|||
var WebSocket = require('ws'); // jshint ignore:line
|
|||
var web3 = require('./web3'); // jshint ignore:line
|
|||
} |
|||
|
|||
var AutoProvider = function (userOptions) { |
|||
if (web3.haveProvider()) { |
|||
return; |
|||
} |
|||
|
|||
// before we determine what provider we are, we have to cache request
|
|||
this.sendQueue = []; |
|||
this.onmessageQueue = []; |
|||
|
|||
if (navigator.qt) { |
|||
this.provider = new web3.providers.QtProvider(); |
|||
return; |
|||
} |
|||
|
|||
userOptions = userOptions || {}; |
|||
var options = { |
|||
httprpc: userOptions.httprpc || 'http://localhost:8080', |
|||
websockets: userOptions.websockets || 'ws://localhost:40404/eth' |
|||
}; |
|||
|
|||
var self = this; |
|||
var closeWithSuccess = function (success) { |
|||
ws.close(); |
|||
if (success) { |
|||
self.provider = new web3.providers.WebSocketProvider(options.websockets); |
|||
} else { |
|||
self.provider = new web3.providers.HttpRpcProvider(options.httprpc); |
|||
self.poll = self.provider.poll.bind(self.provider); |
|||
} |
|||
self.sendQueue.forEach(function (payload) { |
|||
self.provider(payload); |
|||
}); |
|||
self.onmessageQueue.forEach(function (handler) { |
|||
self.provider.onmessage = handler; |
|||
}); |
|||
}; |
|||
|
|||
var ws = new WebSocket(options.websockets); |
|||
|
|||
ws.onopen = function() { |
|||
closeWithSuccess(true); |
|||
}; |
|||
|
|||
ws.onerror = function() { |
|||
closeWithSuccess(false); |
|||
}; |
|||
}; |
|||
|
|||
AutoProvider.prototype.send = function (payload) { |
|||
if (this.provider) { |
|||
this.provider.send(payload); |
|||
return; |
|||
} |
|||
this.sendQueue.push(payload); |
|||
}; |
|||
|
|||
Object.defineProperty(AutoProvider.prototype, 'onmessage', { |
|||
set: function (handler) { |
|||
if (this.provider) { |
|||
this.provider.onmessage = handler; |
|||
return; |
|||
} |
|||
this.onmessageQueue.push(handler); |
|||
} |
|||
}); |
|||
|
|||
module.exports = AutoProvider; |
@ -0,0 +1,56 @@ |
|||
/* |
|||
This file is part of ethereum.js. |
|||
|
|||
ethereum.js is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Lesser General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
ethereum.js is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public License |
|||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file const.js |
|||
* @authors: |
|||
* Marek Kotewicz <marek@ethdev.com> |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
/// required to define ETH_BIGNUMBER_ROUNDING_MODE
|
|||
if (process.env.NODE_ENV !== 'build') { |
|||
var BigNumber = require('bignumber.js'); // jshint ignore:line
|
|||
} |
|||
|
|||
var ETH_UNITS = [ |
|||
'wei', |
|||
'Kwei', |
|||
'Mwei', |
|||
'Gwei', |
|||
'szabo', |
|||
'finney', |
|||
'ether', |
|||
'grand', |
|||
'Mether', |
|||
'Gether', |
|||
'Tether', |
|||
'Pether', |
|||
'Eether', |
|||
'Zether', |
|||
'Yether', |
|||
'Nether', |
|||
'Dether', |
|||
'Vether', |
|||
'Uether' |
|||
]; |
|||
|
|||
module.exports = { |
|||
ETH_PADDING: 32, |
|||
ETH_SIGNATURE_LENGTH: 4, |
|||
ETH_UNITS: ETH_UNITS, |
|||
ETH_BIGNUMBER_ROUNDING_MODE: { ROUNDING_MODE: BigNumber.ROUND_DOWN } |
|||
}; |
|||
|
@ -0,0 +1,135 @@ |
|||
/* |
|||
This file is part of ethereum.js. |
|||
|
|||
ethereum.js is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Lesser General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
ethereum.js is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public License |
|||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file event.js |
|||
* @authors: |
|||
* Marek Kotewicz <marek@ethdev.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
var abi = require('./abi'); |
|||
var utils = require('./utils'); |
|||
|
|||
/// filter inputs array && returns only indexed (or not) inputs
|
|||
/// @param inputs array
|
|||
/// @param bool if result should be an array of indexed params on not
|
|||
/// @returns array of (not?) indexed params
|
|||
var filterInputs = function (inputs, indexed) { |
|||
return inputs.filter(function (current) { |
|||
return current.indexed === indexed; |
|||
}); |
|||
}; |
|||
|
|||
var inputWithName = function (inputs, name) { |
|||
var index = utils.findIndex(inputs, function (input) { |
|||
return input.name === name; |
|||
}); |
|||
|
|||
if (index === -1) { |
|||
console.error('indexed param with name ' + name + ' not found'); |
|||
return undefined; |
|||
} |
|||
return inputs[index]; |
|||
}; |
|||
|
|||
var indexedParamsToTopics = function (event, indexed) { |
|||
// sort keys?
|
|||
return Object.keys(indexed).map(function (key) { |
|||
var inputs = [inputWithName(filterInputs(event.inputs, true), key)]; |
|||
|
|||
var value = indexed[key]; |
|||
if (value instanceof Array) { |
|||
return value.map(function (v) { |
|||
return abi.formatInput(inputs, [v]); |
|||
}); |
|||
} |
|||
return abi.formatInput(inputs, [value]); |
|||
}); |
|||
}; |
|||
|
|||
var inputParser = function (address, signature, event) { |
|||
|
|||
// valid options are 'earliest', 'latest', 'offset' and 'max', as defined for 'eth.watch'
|
|||
return function (indexed, options) { |
|||
var o = options || {}; |
|||
o.address = address; |
|||
o.topic = []; |
|||
o.topic.push(signature); |
|||
if (indexed) { |
|||
o.topic = o.topic.concat(indexedParamsToTopics(event, indexed)); |
|||
} |
|||
return o; |
|||
}; |
|||
}; |
|||
|
|||
var getArgumentsObject = function (inputs, indexed, notIndexed) { |
|||
var indexedCopy = indexed.slice(); |
|||
var notIndexedCopy = notIndexed.slice(); |
|||
return inputs.reduce(function (acc, current) { |
|||
var value; |
|||
if (current.indexed) |
|||
value = indexed.splice(0, 1)[0]; |
|||
else |
|||
value = notIndexed.splice(0, 1)[0]; |
|||
|
|||
acc[current.name] = value; |
|||
return acc; |
|||
}, {}); |
|||
}; |
|||
|
|||
var outputParser = function (event) { |
|||
|
|||
return function (output) { |
|||
var result = { |
|||
event: utils.extractDisplayName(event.name), |
|||
number: output.number, |
|||
args: {} |
|||
}; |
|||
|
|||
if (!output.topic) { |
|||
return result; |
|||
} |
|||
|
|||
var indexedOutputs = filterInputs(event.inputs, true); |
|||
var indexedData = "0x" + output.topic.slice(1, output.topic.length).map(function (topic) { return topic.slice(2); }).join(""); |
|||
var indexedRes = abi.formatOutput(indexedOutputs, indexedData); |
|||
|
|||
var notIndexedOutputs = filterInputs(event.inputs, false); |
|||
var notIndexedRes = abi.formatOutput(notIndexedOutputs, output.data); |
|||
|
|||
result.args = getArgumentsObject(event.inputs, indexedRes, notIndexedRes); |
|||
|
|||
return result; |
|||
}; |
|||
}; |
|||
|
|||
var getMatchingEvent = function (events, payload) { |
|||
for (var i = 0; i < events.length; i++) { |
|||
var signature = abi.eventSignatureFromAscii(events[i].name); |
|||
if (signature === payload.topic[0]) { |
|||
return events[i]; |
|||
} |
|||
} |
|||
return undefined; |
|||
}; |
|||
|
|||
|
|||
module.exports = { |
|||
inputParser: inputParser, |
|||
outputParser: outputParser, |
|||
getMatchingEvent: getMatchingEvent |
|||
}; |
|||
|
@ -0,0 +1,101 @@ |
|||
/* |
|||
This file is part of ethereum.js. |
|||
|
|||
ethereum.js is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Lesser General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
ethereum.js is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public License |
|||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file filter.js |
|||
* @authors: |
|||
* Jeffrey Wilcke <jeff@ethdev.com> |
|||
* Marek Kotewicz <marek@ethdev.com> |
|||
* Marian Oancea <marian@ethdev.com> |
|||
* Gav Wood <g@ethdev.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
var web3 = require('./web3'); // jshint ignore:line
|
|||
|
|||
/// should be used when we want to watch something
|
|||
/// it's using inner polling mechanism and is notified about changes
|
|||
/// TODO: change 'options' name cause it may be not the best matching one, since we have events
|
|||
var Filter = function(options, impl) { |
|||
|
|||
if (typeof options !== "string") { |
|||
|
|||
// topics property is deprecated, warn about it!
|
|||
if (options.topics) { |
|||
console.warn('"topics" is deprecated, use "topic" instead'); |
|||
} |
|||
|
|||
this._onWatchResult = options._onWatchEventResult; |
|||
|
|||
// evaluate lazy properties
|
|||
options = { |
|||
to: options.to, |
|||
topic: options.topic, |
|||
earliest: options.earliest, |
|||
latest: options.latest, |
|||
max: options.max, |
|||
skip: options.skip, |
|||
address: options.address |
|||
}; |
|||
|
|||
} |
|||
|
|||
this.impl = impl; |
|||
this.callbacks = []; |
|||
|
|||
this.id = impl.newFilter(options); |
|||
web3.provider.startPolling({method: impl.changed, params: [this.id]}, this.id, this.trigger.bind(this)); |
|||
}; |
|||
|
|||
/// alias for changed*
|
|||
Filter.prototype.arrived = function(callback) { |
|||
this.changed(callback); |
|||
}; |
|||
Filter.prototype.happened = function(callback) { |
|||
this.changed(callback); |
|||
}; |
|||
|
|||
/// gets called when there is new eth/shh message
|
|||
Filter.prototype.changed = function(callback) { |
|||
this.callbacks.push(callback); |
|||
}; |
|||
|
|||
/// trigger calling new message from people
|
|||
Filter.prototype.trigger = function(messages) { |
|||
for (var i = 0; i < this.callbacks.length; i++) { |
|||
for (var j = 0; j < messages.length; j++) { |
|||
var message = this._onWatchResult ? this._onWatchResult(messages[j]) : messages[j]; |
|||
this.callbacks[i].call(this, message); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
/// should be called to uninstall current filter
|
|||
Filter.prototype.uninstall = function() { |
|||
this.impl.uninstallFilter(this.id); |
|||
web3.provider.stopPolling(this.id); |
|||
}; |
|||
|
|||
/// should be called to manually trigger getting latest messages from the client
|
|||
Filter.prototype.messages = function() { |
|||
return this.impl.getMessages(this.id); |
|||
}; |
|||
|
|||
/// alias for messages
|
|||
Filter.prototype.logs = function () { |
|||
return this.messages(); |
|||
}; |
|||
|
|||
module.exports = Filter; |
@ -0,0 +1,154 @@ |
|||
/* |
|||
This file is part of ethereum.js. |
|||
|
|||
ethereum.js is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Lesser General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
ethereum.js is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public License |
|||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file formatters.js |
|||
* @authors: |
|||
* Marek Kotewicz <marek@ethdev.com> |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
if (process.env.NODE_ENV !== 'build') { |
|||
var BigNumber = require('bignumber.js'); // jshint ignore:line
|
|||
} |
|||
|
|||
var utils = require('./utils'); |
|||
var c = require('./const'); |
|||
|
|||
/// @param string string to be padded
|
|||
/// @param number of characters that result string should have
|
|||
/// @param sign, by default 0
|
|||
/// @returns right aligned string
|
|||
var padLeft = function (string, chars, sign) { |
|||
return new Array(chars - string.length + 1).join(sign ? sign : "0") + string; |
|||
}; |
|||
|
|||
/// Formats input value to byte representation of int
|
|||
/// If value is negative, return it's two's complement
|
|||
/// If the value is floating point, round it down
|
|||
/// @returns right-aligned byte representation of int
|
|||
var formatInputInt = function (value) { |
|||
var padding = c.ETH_PADDING * 2; |
|||
if (value instanceof BigNumber || typeof value === 'number') { |
|||
if (typeof value === 'number') |
|||
value = new BigNumber(value); |
|||
BigNumber.config(c.ETH_BIGNUMBER_ROUNDING_MODE); |
|||
value = value.round(); |
|||
|
|||
if (value.lessThan(0)) |
|||
value = new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16).plus(value).plus(1); |
|||
value = value.toString(16); |
|||
} |
|||
else if (value.indexOf('0x') === 0) |
|||
value = value.substr(2); |
|||
else if (typeof value === 'string') |
|||
value = formatInputInt(new BigNumber(value)); |
|||
else |
|||
value = (+value).toString(16); |
|||
return padLeft(value, padding); |
|||
}; |
|||
|
|||
/// Formats input value to byte representation of string
|
|||
/// @returns left-algined byte representation of string
|
|||
var formatInputString = function (value) { |
|||
return utils.fromAscii(value, c.ETH_PADDING).substr(2); |
|||
}; |
|||
|
|||
/// Formats input value to byte representation of bool
|
|||
/// @returns right-aligned byte representation bool
|
|||
var formatInputBool = function (value) { |
|||
return '000000000000000000000000000000000000000000000000000000000000000' + (value ? '1' : '0'); |
|||
}; |
|||
|
|||
/// Formats input value to byte representation of real
|
|||
/// Values are multiplied by 2^m and encoded as integers
|
|||
/// @returns byte representation of real
|
|||
var formatInputReal = function (value) { |
|||
return formatInputInt(new BigNumber(value).times(new BigNumber(2).pow(128))); |
|||
}; |
|||
|
|||
|
|||
/// Check if input value is negative
|
|||
/// @param value is hex format
|
|||
/// @returns true if it is negative, otherwise false
|
|||
var signedIsNegative = function (value) { |
|||
return (new BigNumber(value.substr(0, 1), 16).toString(2).substr(0, 1)) === '1'; |
|||
}; |
|||
|
|||
/// Formats input right-aligned input bytes to int
|
|||
/// @returns right-aligned input bytes formatted to int
|
|||
var formatOutputInt = function (value) { |
|||
value = value || "0"; |
|||
// check if it's negative number
|
|||
// it it is, return two's complement
|
|||
if (signedIsNegative(value)) { |
|||
return new BigNumber(value, 16).minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)).minus(1); |
|||
} |
|||
return new BigNumber(value, 16); |
|||
}; |
|||
|
|||
/// Formats big right-aligned input bytes to uint
|
|||
/// @returns right-aligned input bytes formatted to uint
|
|||
var formatOutputUInt = function (value) { |
|||
value = value || "0"; |
|||
return new BigNumber(value, 16); |
|||
}; |
|||
|
|||
/// @returns input bytes formatted to real
|
|||
var formatOutputReal = function (value) { |
|||
return formatOutputInt(value).dividedBy(new BigNumber(2).pow(128)); |
|||
}; |
|||
|
|||
/// @returns input bytes formatted to ureal
|
|||
var formatOutputUReal = function (value) { |
|||
return formatOutputUInt(value).dividedBy(new BigNumber(2).pow(128)); |
|||
}; |
|||
|
|||
/// @returns right-aligned input bytes formatted to hex
|
|||
var formatOutputHash = function (value) { |
|||
return "0x" + value; |
|||
}; |
|||
|
|||
/// @returns right-aligned input bytes formatted to bool
|
|||
var formatOutputBool = function (value) { |
|||
return value === '0000000000000000000000000000000000000000000000000000000000000001' ? true : false; |
|||
}; |
|||
|
|||
/// @returns left-aligned input bytes formatted to ascii string
|
|||
var formatOutputString = function (value) { |
|||
return utils.toAscii(value); |
|||
}; |
|||
|
|||
/// @returns right-aligned input bytes formatted to address
|
|||
var formatOutputAddress = function (value) { |
|||
return "0x" + value.slice(value.length - 40, value.length); |
|||
}; |
|||
|
|||
|
|||
module.exports = { |
|||
formatInputInt: formatInputInt, |
|||
formatInputString: formatInputString, |
|||
formatInputBool: formatInputBool, |
|||
formatInputReal: formatInputReal, |
|||
formatOutputInt: formatOutputInt, |
|||
formatOutputUInt: formatOutputUInt, |
|||
formatOutputReal: formatOutputReal, |
|||
formatOutputUReal: formatOutputUReal, |
|||
formatOutputHash: formatOutputHash, |
|||
formatOutputBool: formatOutputBool, |
|||
formatOutputString: formatOutputString, |
|||
formatOutputAddress: formatOutputAddress |
|||
}; |
|||
|
@ -1,94 +0,0 @@ |
|||
/* |
|||
This file is part of ethereum.js. |
|||
|
|||
ethereum.js is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Lesser General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
ethereum.js is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public License |
|||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file httprpc.js |
|||
* @authors: |
|||
* Marek Kotewicz <marek@ethdev.com> |
|||
* Marian Oancea <marian@ethdev.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
// TODO: is these line is supposed to be here?
|
|||
if (process.env.NODE_ENV !== 'build') { |
|||
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
|
|||
} |
|||
|
|||
var HttpRpcProvider = function (host) { |
|||
this.handlers = []; |
|||
this.host = host; |
|||
}; |
|||
|
|||
function formatJsonRpcObject(object) { |
|||
return { |
|||
jsonrpc: '2.0', |
|||
method: object.call, |
|||
params: object.args, |
|||
id: object._id |
|||
}; |
|||
} |
|||
|
|||
function formatJsonRpcMessage(message) { |
|||
var object = JSON.parse(message); |
|||
|
|||
return { |
|||
_id: object.id, |
|||
data: object.result, |
|||
error: object.error |
|||
}; |
|||
} |
|||
|
|||
HttpRpcProvider.prototype.sendRequest = function (payload, cb) { |
|||
var data = formatJsonRpcObject(payload); |
|||
|
|||
var request = new XMLHttpRequest(); |
|||
request.open("POST", this.host, true); |
|||
request.send(JSON.stringify(data)); |
|||
request.onreadystatechange = function () { |
|||
if (request.readyState === 4 && cb) { |
|||
cb(request); |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
HttpRpcProvider.prototype.send = function (payload) { |
|||
var self = this; |
|||
this.sendRequest(payload, function (request) { |
|||
self.handlers.forEach(function (handler) { |
|||
handler.call(self, formatJsonRpcMessage(request.responseText)); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
HttpRpcProvider.prototype.poll = function (payload, id) { |
|||
var self = this; |
|||
this.sendRequest(payload, function (request) { |
|||
var parsed = JSON.parse(request.responseText); |
|||
if (parsed.error || (parsed.result instanceof Array ? parsed.result.length === 0 : !parsed.result)) { |
|||
return; |
|||
} |
|||
self.handlers.forEach(function (handler) { |
|||
handler.call(self, {_event: payload.call, _id: id, data: parsed.result}); |
|||
}); |
|||
}); |
|||
}; |
|||
|
|||
Object.defineProperty(HttpRpcProvider.prototype, "onmessage", { |
|||
set: function (handler) { |
|||
this.handlers.push(handler); |
|||
} |
|||
}); |
|||
|
|||
module.exports = HttpRpcProvider; |
@ -0,0 +1,46 @@ |
|||
/* |
|||
This file is part of ethereum.js. |
|||
|
|||
ethereum.js is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Lesser General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
ethereum.js is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public License |
|||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file httpsync.js |
|||
* @authors: |
|||
* Marek Kotewicz <marek@ethdev.com> |
|||
* Marian Oancea <marian@ethdev.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
if (process.env.NODE_ENV !== 'build') { |
|||
var XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest; // jshint ignore:line
|
|||
} |
|||
|
|||
var HttpSyncProvider = function (host) { |
|||
this.handlers = []; |
|||
this.host = host || 'http://localhost:8080'; |
|||
}; |
|||
|
|||
HttpSyncProvider.prototype.send = function (payload) { |
|||
//var data = formatJsonRpcObject(payload);
|
|||
|
|||
var request = new XMLHttpRequest(); |
|||
request.open('POST', this.host, false); |
|||
request.send(JSON.stringify(payload)); |
|||
|
|||
// check request.status
|
|||
var result = request.responseText; |
|||
return JSON.parse(result); |
|||
}; |
|||
|
|||
module.exports = HttpSyncProvider; |
|||
|
@ -0,0 +1,65 @@ |
|||
/* |
|||
This file is part of ethereum.js. |
|||
|
|||
ethereum.js is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Lesser General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
ethereum.js is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public License |
|||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file jsonrpc.js |
|||
* @authors: |
|||
* Marek Kotewicz <marek@ethdev.com> |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
var messageId = 1; |
|||
|
|||
/// Should be called to valid json create payload object
|
|||
/// @param method of jsonrpc call, required
|
|||
/// @param params, an array of method params, optional
|
|||
/// @returns valid jsonrpc payload object
|
|||
var toPayload = function (method, params) { |
|||
if (!method) |
|||
console.error('jsonrpc method should be specified!'); |
|||
|
|||
return { |
|||
jsonrpc: '2.0', |
|||
method: method, |
|||
params: params || [], |
|||
id: messageId++ |
|||
}; |
|||
}; |
|||
|
|||
/// Should be called to check if jsonrpc response is valid
|
|||
/// @returns true if response is valid, otherwise false
|
|||
var isValidResponse = function (response) { |
|||
return !!response && |
|||
!response.error && |
|||
response.jsonrpc === '2.0' && |
|||
typeof response.id === 'number' && |
|||
response.result !== undefined; // only undefined is not valid json object
|
|||
}; |
|||
|
|||
/// Should be called to create batch payload object
|
|||
/// @param messages, an array of objects with method (required) and params (optional) fields
|
|||
var toBatchPayload = function (messages) { |
|||
return messages.map(function (message) { |
|||
return toPayload(message.method, message.params); |
|||
}); |
|||
}; |
|||
|
|||
module.exports = { |
|||
toPayload: toPayload, |
|||
isValidResponse: isValidResponse, |
|||
toBatchPayload: toBatchPayload |
|||
}; |
|||
|
|||
|
@ -0,0 +1,18 @@ |
|||
var addressName = {"0x12378912345789": "Gav", "0x57835893478594739854": "Jeff"}; |
|||
var nameAddress = {}; |
|||
|
|||
for (var prop in addressName) { |
|||
if (addressName.hasOwnProperty(prop)) { |
|||
nameAddress[addressName[prop]] = prop; |
|||
} |
|||
} |
|||
|
|||
var local = { |
|||
addressBook:{ |
|||
byName: addressName, |
|||
byAddress: nameAddress |
|||
} |
|||
}; |
|||
|
|||
if (typeof(module) !== "undefined") |
|||
module.exports = local; |
@ -0,0 +1,102 @@ |
|||
/* |
|||
This file is part of ethereum.js. |
|||
|
|||
ethereum.js is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Lesser General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
ethereum.js is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public License |
|||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file providermanager.js |
|||
* @authors: |
|||
* Jeffrey Wilcke <jeff@ethdev.com> |
|||
* Marek Kotewicz <marek@ethdev.com> |
|||
* Marian Oancea <marian@ethdev.com> |
|||
* Gav Wood <g@ethdev.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
var web3 = require('./web3'); |
|||
var jsonrpc = require('./jsonrpc'); |
|||
|
|||
|
|||
/** |
|||
* Provider manager object prototype |
|||
* It's responsible for passing messages to providers |
|||
* If no provider is set it's responsible for queuing requests |
|||
* It's also responsible for polling the ethereum node for incoming messages |
|||
* Default poll timeout is 12 seconds |
|||
* If we are running ethereum.js inside ethereum browser, there are backend based tools responsible for polling, |
|||
* and provider manager polling mechanism is not used |
|||
*/ |
|||
var ProviderManager = function() { |
|||
this.polls = []; |
|||
this.provider = undefined; |
|||
|
|||
var self = this; |
|||
var poll = function () { |
|||
self.polls.forEach(function (data) { |
|||
var result = self.send(data.data); |
|||
|
|||
if (!(result instanceof Array) || result.length === 0) { |
|||
return; |
|||
} |
|||
|
|||
data.callback(result); |
|||
}); |
|||
|
|||
setTimeout(poll, 1000); |
|||
}; |
|||
poll(); |
|||
}; |
|||
|
|||
/// sends outgoing requests
|
|||
/// @params data - an object with at least 'method' property
|
|||
ProviderManager.prototype.send = function(data) { |
|||
var payload = jsonrpc.toPayload(data.method, data.params); |
|||
|
|||
if (this.provider === undefined) { |
|||
console.error('provider is not set'); |
|||
return null; |
|||
} |
|||
|
|||
var result = this.provider.send(payload); |
|||
|
|||
if (!jsonrpc.isValidResponse(result)) { |
|||
console.log(result); |
|||
return null; |
|||
} |
|||
|
|||
return result.result; |
|||
}; |
|||
|
|||
/// setups provider, which will be used for sending messages
|
|||
ProviderManager.prototype.set = function(provider) { |
|||
this.provider = provider; |
|||
}; |
|||
|
|||
/// this method is only used, when we do not have native qt bindings and have to do polling on our own
|
|||
/// should be callled, on start watching for eth/shh changes
|
|||
ProviderManager.prototype.startPolling = function (data, pollId, callback) { |
|||
this.polls.push({data: data, id: pollId, callback: callback}); |
|||
}; |
|||
|
|||
/// should be called to stop polling for certain watch changes
|
|||
ProviderManager.prototype.stopPolling = function (pollId) { |
|||
for (var i = this.polls.length; i--;) { |
|||
var poll = this.polls[i]; |
|||
if (poll.id === pollId) { |
|||
this.polls.splice(i, 1); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
module.exports = ProviderManager; |
|||
|
@ -0,0 +1,79 @@ |
|||
/* |
|||
This file is part of ethereum.js. |
|||
|
|||
ethereum.js is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Lesser General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
ethereum.js is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public License |
|||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file types.js |
|||
* @authors: |
|||
* Marek Kotewicz <marek@ethdev.com> |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
var f = require('./formatters'); |
|||
|
|||
/// @param expected type prefix (string)
|
|||
/// @returns function which checks if type has matching prefix. if yes, returns true, otherwise false
|
|||
var prefixedType = function (prefix) { |
|||
return function (type) { |
|||
return type.indexOf(prefix) === 0; |
|||
}; |
|||
}; |
|||
|
|||
/// @param expected type name (string)
|
|||
/// @returns function which checks if type is matching expected one. if yes, returns true, otherwise false
|
|||
var namedType = function (name) { |
|||
return function (type) { |
|||
return name === type; |
|||
}; |
|||
}; |
|||
|
|||
/// Setups input formatters for solidity types
|
|||
/// @returns an array of input formatters
|
|||
var inputTypes = function () { |
|||
|
|||
return [ |
|||
{ type: prefixedType('uint'), format: f.formatInputInt }, |
|||
{ type: prefixedType('int'), format: f.formatInputInt }, |
|||
{ type: prefixedType('hash'), format: f.formatInputInt }, |
|||
{ type: prefixedType('string'), format: f.formatInputString }, |
|||
{ type: prefixedType('real'), format: f.formatInputReal }, |
|||
{ type: prefixedType('ureal'), format: f.formatInputReal }, |
|||
{ type: namedType('address'), format: f.formatInputInt }, |
|||
{ type: namedType('bool'), format: f.formatInputBool } |
|||
]; |
|||
}; |
|||
|
|||
/// Setups output formaters for solidity types
|
|||
/// @returns an array of output formatters
|
|||
var outputTypes = function () { |
|||
|
|||
return [ |
|||
{ type: prefixedType('uint'), format: f.formatOutputUInt }, |
|||
{ type: prefixedType('int'), format: f.formatOutputInt }, |
|||
{ type: prefixedType('hash'), format: f.formatOutputHash }, |
|||
{ type: prefixedType('string'), format: f.formatOutputString }, |
|||
{ type: prefixedType('real'), format: f.formatOutputReal }, |
|||
{ type: prefixedType('ureal'), format: f.formatOutputUReal }, |
|||
{ type: namedType('address'), format: f.formatOutputAddress }, |
|||
{ type: namedType('bool'), format: f.formatOutputBool } |
|||
]; |
|||
}; |
|||
|
|||
module.exports = { |
|||
prefixedType: prefixedType, |
|||
namedType: namedType, |
|||
inputTypes: inputTypes, |
|||
outputTypes: outputTypes |
|||
}; |
|||
|
@ -0,0 +1,142 @@ |
|||
/* |
|||
This file is part of ethereum.js. |
|||
|
|||
ethereum.js is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Lesser General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
ethereum.js is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public License |
|||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file utils.js |
|||
* @authors: |
|||
* Marek Kotewicz <marek@ethdev.com> |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
var c = require('./const'); |
|||
|
|||
/// Finds first index of array element matching pattern
|
|||
/// @param array
|
|||
/// @param callback pattern
|
|||
/// @returns index of element
|
|||
var findIndex = function (array, callback) { |
|||
var end = false; |
|||
var i = 0; |
|||
for (; i < array.length && !end; i++) { |
|||
end = callback(array[i]); |
|||
} |
|||
return end ? i - 1 : -1; |
|||
}; |
|||
|
|||
/// @returns ascii string representation of hex value prefixed with 0x
|
|||
var toAscii = function(hex) { |
|||
// Find termination
|
|||
var str = ""; |
|||
var i = 0, l = hex.length; |
|||
if (hex.substring(0, 2) === '0x') { |
|||
i = 2; |
|||
} |
|||
for (; i < l; i+=2) { |
|||
var code = parseInt(hex.substr(i, 2), 16); |
|||
if (code === 0) { |
|||
break; |
|||
} |
|||
|
|||
str += String.fromCharCode(code); |
|||
} |
|||
|
|||
return str; |
|||
}; |
|||
|
|||
var toHex = function(str) { |
|||
var hex = ""; |
|||
for(var i = 0; i < str.length; i++) { |
|||
var n = str.charCodeAt(i).toString(16); |
|||
hex += n.length < 2 ? '0' + n : n; |
|||
} |
|||
|
|||
return hex; |
|||
}; |
|||
|
|||
/// @returns hex representation (prefixed by 0x) of ascii string
|
|||
var fromAscii = function(str, pad) { |
|||
pad = pad === undefined ? 0 : pad; |
|||
var hex = toHex(str); |
|||
while (hex.length < pad*2) |
|||
hex += "00"; |
|||
return "0x" + hex; |
|||
}; |
|||
|
|||
/// @returns display name for function/event eg. multiply(uint256) -> multiply
|
|||
var extractDisplayName = function (name) { |
|||
var length = name.indexOf('('); |
|||
return length !== -1 ? name.substr(0, length) : name; |
|||
}; |
|||
|
|||
/// @returns overloaded part of function/event name
|
|||
var extractTypeName = function (name) { |
|||
/// TODO: make it invulnerable
|
|||
var length = name.indexOf('('); |
|||
return length !== -1 ? name.substr(length + 1, name.length - 1 - (length + 1)).replace(' ', '') : ""; |
|||
}; |
|||
|
|||
/// Filters all function from input abi
|
|||
/// @returns abi array with filtered objects of type 'function'
|
|||
var filterFunctions = function (json) { |
|||
return json.filter(function (current) { |
|||
return current.type === 'function'; |
|||
}); |
|||
}; |
|||
|
|||
/// Filters all events form input abi
|
|||
/// @returns abi array with filtered objects of type 'event'
|
|||
var filterEvents = function (json) { |
|||
return json.filter(function (current) { |
|||
return current.type === 'event'; |
|||
}); |
|||
}; |
|||
|
|||
/// used to transform value/string to eth string
|
|||
/// TODO: use BigNumber.js to parse int
|
|||
/// TODO: add tests for it!
|
|||
var toEth = function (str) { |
|||
var val = typeof str === "string" ? str.indexOf('0x') === 0 ? parseInt(str.substr(2), 16) : parseInt(str) : str; |
|||
var unit = 0; |
|||
var units = c.ETH_UNITS; |
|||
while (val > 3000 && unit < units.length - 1) |
|||
{ |
|||
val /= 1000; |
|||
unit++; |
|||
} |
|||
var s = val.toString().length < val.toFixed(2).length ? val.toString() : val.toFixed(2); |
|||
var replaceFunction = function($0, $1, $2) { |
|||
return $1 + ',' + $2; |
|||
}; |
|||
|
|||
while (true) { |
|||
var o = s; |
|||
s = s.replace(/(\d)(\d\d\d[\.\,])/, replaceFunction); |
|||
if (o === s) |
|||
break; |
|||
} |
|||
return s + ' ' + units[unit]; |
|||
}; |
|||
|
|||
module.exports = { |
|||
findIndex: findIndex, |
|||
toAscii: toAscii, |
|||
fromAscii: fromAscii, |
|||
extractDisplayName: extractDisplayName, |
|||
extractTypeName: extractTypeName, |
|||
filterFunctions: filterFunctions, |
|||
filterEvents: filterEvents, |
|||
toEth: toEth |
|||
}; |
|||
|
@ -1,78 +0,0 @@ |
|||
/* |
|||
This file is part of ethereum.js. |
|||
|
|||
ethereum.js is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU Lesser General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
ethereum.js is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU Lesser General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU Lesser General Public License |
|||
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file websocket.js |
|||
* @authors: |
|||
* Jeffrey Wilcke <jeff@ethdev.com> |
|||
* Marek Kotewicz <marek@ethdev.com> |
|||
* Marian Oancea <marian@ethdev.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
// TODO: is these line is supposed to be here?
|
|||
if (process.env.NODE_ENV !== 'build') { |
|||
var WebSocket = require('ws'); // jshint ignore:line
|
|||
} |
|||
|
|||
var WebSocketProvider = function(host) { |
|||
// onmessage handlers
|
|||
this.handlers = []; |
|||
// queue will be filled with messages if send is invoked before the ws is ready
|
|||
this.queued = []; |
|||
this.ready = false; |
|||
|
|||
this.ws = new WebSocket(host); |
|||
|
|||
var self = this; |
|||
this.ws.onmessage = function(event) { |
|||
for(var i = 0; i < self.handlers.length; i++) { |
|||
self.handlers[i].call(self, JSON.parse(event.data), event); |
|||
} |
|||
}; |
|||
|
|||
this.ws.onopen = function() { |
|||
self.ready = true; |
|||
|
|||
for(var i = 0; i < self.queued.length; i++) { |
|||
// Resend
|
|||
self.send(self.queued[i]); |
|||
} |
|||
}; |
|||
}; |
|||
|
|||
WebSocketProvider.prototype.send = function(payload) { |
|||
if(this.ready) { |
|||
var data = JSON.stringify(payload); |
|||
|
|||
this.ws.send(data); |
|||
} else { |
|||
this.queued.push(payload); |
|||
} |
|||
}; |
|||
|
|||
WebSocketProvider.prototype.onMessage = function(handler) { |
|||
this.handlers.push(handler); |
|||
}; |
|||
|
|||
WebSocketProvider.prototype.unload = function() { |
|||
this.ws.close(); |
|||
}; |
|||
Object.defineProperty(WebSocketProvider.prototype, "onmessage", { |
|||
set: function(provider) { this.onMessage(provider); } |
|||
}); |
|||
|
|||
if (typeof(module) !== "undefined") |
|||
module.exports = WebSocketProvider; |
@ -0,0 +1,427 @@ |
|||
var assert = require('assert'); |
|||
var BigNumber = require('bignumber.js'); |
|||
var abi = require('../lib/abi.js'); |
|||
var clone = function (object) { return JSON.parse(JSON.stringify(object)); }; |
|||
|
|||
var description = [{ |
|||
"name": "test", |
|||
"type": "function", |
|||
"inputs": [{ |
|||
"name": "a", |
|||
"type": "uint256" |
|||
} |
|||
], |
|||
"outputs": [ |
|||
{ |
|||
"name": "d", |
|||
"type": "uint256" |
|||
} |
|||
] |
|||
}]; |
|||
|
|||
describe('abi', function() { |
|||
describe('inputParser', function() { |
|||
it('should parse input uint', function() { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: "uint" } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001"); |
|||
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a"); |
|||
assert.equal( |
|||
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
); |
|||
assert.equal( |
|||
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)), |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
); |
|||
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000"); |
|||
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003"); |
|||
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000"); |
|||
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003"); |
|||
|
|||
}); |
|||
|
|||
it('should parse input uint128', function() { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: "uint128" } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001"); |
|||
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a"); |
|||
assert.equal( |
|||
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
); |
|||
assert.equal( |
|||
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)), |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
); |
|||
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000"); |
|||
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003"); |
|||
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000"); |
|||
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003"); |
|||
|
|||
}); |
|||
|
|||
it('should parse input uint256', function() { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: "uint256" } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001"); |
|||
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a"); |
|||
assert.equal( |
|||
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
); |
|||
assert.equal( |
|||
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)), |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
); |
|||
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000"); |
|||
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003"); |
|||
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000"); |
|||
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003"); |
|||
|
|||
}); |
|||
|
|||
it('should parse input int', function() { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: "int" } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001"); |
|||
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a"); |
|||
assert.equal(parser.test(-1), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); |
|||
assert.equal(parser.test(-2), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"); |
|||
assert.equal(parser.test(-16), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0"); |
|||
assert.equal( |
|||
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
); |
|||
assert.equal( |
|||
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)), |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
); |
|||
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000"); |
|||
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003"); |
|||
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000"); |
|||
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003"); |
|||
}); |
|||
|
|||
it('should parse input int128', function() { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: "int128" } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001"); |
|||
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a"); |
|||
assert.equal(parser.test(-1), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); |
|||
assert.equal(parser.test(-2), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"); |
|||
assert.equal(parser.test(-16), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0"); |
|||
assert.equal( |
|||
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
); |
|||
assert.equal( |
|||
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)), |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
); |
|||
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000"); |
|||
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003"); |
|||
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000"); |
|||
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003"); |
|||
|
|||
}); |
|||
|
|||
it('should parse input int256', function() { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: "int256" } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001"); |
|||
assert.equal(parser.test(10), "000000000000000000000000000000000000000000000000000000000000000a"); |
|||
assert.equal(parser.test(-1), "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); |
|||
assert.equal(parser.test(-2), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"); |
|||
assert.equal(parser.test(-16), "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0"); |
|||
assert.equal( |
|||
parser.test("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
); |
|||
assert.equal( |
|||
parser.test(new BigNumber("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16)), |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" |
|||
); |
|||
assert.equal(parser.test(0.1), "0000000000000000000000000000000000000000000000000000000000000000"); |
|||
assert.equal(parser.test(3.9), "0000000000000000000000000000000000000000000000000000000000000003"); |
|||
assert.equal(parser.test('0.1'), "0000000000000000000000000000000000000000000000000000000000000000"); |
|||
assert.equal(parser.test('3.9'), "0000000000000000000000000000000000000000000000000000000000000003"); |
|||
|
|||
}); |
|||
|
|||
it('should parse input bool', function() { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: 'bool' } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal(parser.test(true), "0000000000000000000000000000000000000000000000000000000000000001"); |
|||
assert.equal(parser.test(false), "0000000000000000000000000000000000000000000000000000000000000000"); |
|||
|
|||
}); |
|||
|
|||
it('should parse input hash', function() { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: "hash" } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"); |
|||
|
|||
}); |
|||
|
|||
it('should parse input hash256', function() { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: "hash256" } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"); |
|||
|
|||
}); |
|||
|
|||
|
|||
it('should parse input hash160', function() { |
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: "hash160" } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"); |
|||
}); |
|||
|
|||
it('should parse input address', function () { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: "address" } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d) |
|||
|
|||
// then
|
|||
assert.equal(parser.test("0x407d73d8a49eeb85d32cf465507dd71d507100c1"), "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1"); |
|||
|
|||
}); |
|||
|
|||
it('should parse input string', function () { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: "string" } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal( |
|||
parser.test('hello'), |
|||
"000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000" |
|||
); |
|||
assert.equal( |
|||
parser.test('world'), |
|||
"0000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000" |
|||
); |
|||
}); |
|||
|
|||
it('should use proper method name', function () { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
d[0].name = 'helloworld(int)'; |
|||
d[0].inputs = [ |
|||
{ type: "int" } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal(parser.helloworld(1), "0000000000000000000000000000000000000000000000000000000000000001"); |
|||
assert.equal(parser.helloworld['int'](1), "0000000000000000000000000000000000000000000000000000000000000001"); |
|||
|
|||
}); |
|||
|
|||
it('should parse multiple methods', function () { |
|||
|
|||
// given
|
|||
var d = [{ |
|||
name: "test", |
|||
type: "function", |
|||
inputs: [{ type: "int" }], |
|||
outputs: [{ type: "int" }] |
|||
},{ |
|||
name: "test2", |
|||
type: "function", |
|||
inputs: [{ type: "string" }], |
|||
outputs: [{ type: "string" }] |
|||
}]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
//then
|
|||
assert.equal(parser.test(1), "0000000000000000000000000000000000000000000000000000000000000001"); |
|||
assert.equal( |
|||
parser.test2('hello'), |
|||
"000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000" |
|||
); |
|||
|
|||
}); |
|||
|
|||
it('should parse input array of ints', function () { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: "int[]" } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal( |
|||
parser.test([5, 6]), |
|||
"0000000000000000000000000000000000000000000000000000000000000002" + |
|||
"0000000000000000000000000000000000000000000000000000000000000005" + |
|||
"0000000000000000000000000000000000000000000000000000000000000006" |
|||
); |
|||
}); |
|||
|
|||
it('should parse input real', function () { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: 'real' } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal(parser.test([1]), "0000000000000000000000000000000100000000000000000000000000000000"); |
|||
assert.equal(parser.test([2.125]), "0000000000000000000000000000000220000000000000000000000000000000"); |
|||
assert.equal(parser.test([8.5]), "0000000000000000000000000000000880000000000000000000000000000000"); |
|||
assert.equal(parser.test([-1]), "ffffffffffffffffffffffffffffffff00000000000000000000000000000000"); |
|||
|
|||
}); |
|||
|
|||
it('should parse input ureal', function () { |
|||
|
|||
// given
|
|||
var d = clone(description); |
|||
|
|||
d[0].inputs = [ |
|||
{ type: 'ureal' } |
|||
]; |
|||
|
|||
// when
|
|||
var parser = abi.inputParser(d); |
|||
|
|||
// then
|
|||
assert.equal(parser.test([1]), "0000000000000000000000000000000100000000000000000000000000000000"); |
|||
assert.equal(parser.test([2.125]), "0000000000000000000000000000000220000000000000000000000000000000"); |
|||
assert.equal(parser.test([8.5]), "0000000000000000000000000000000880000000000000000000000000000000"); |
|||
|
|||
}); |
|||
|
|||
}); |
|||
}); |
|||
|
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue