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 "Memory.h" |
||||
|
|
||||
#include <vector> |
#include <vector> |
||||
#include <iostream> |
#include <iostream> |
||||
#include <iomanip> |
#include <iomanip> |
||||
#include <cstdint> |
#include <cstdint> |
||||
#include <cassert> |
#include <cassert> |
||||
|
|
||||
#include <llvm/IR/GlobalVariable.h> |
#include <llvm/IR/GlobalVariable.h> |
||||
#include <llvm/IR/Function.h> |
#include <llvm/IR/Function.h> |
||||
#include <llvm/IR/IntrinsicInst.h> |
#include <llvm/IR/IntrinsicInst.h> |
||||
|
|
||||
#include "Type.h" |
#include "Type.h" |
||||
#include "Runtime.h" |
#include "Runtime.h" |
||||
#include "GasMeter.h" |
#include "GasMeter.h" |
||||
#include "Endianness.h" |
#include "Endianness.h" |
||||
#include "RuntimeManager.h" |
#include "RuntimeManager.h" |
||||
|
|
||||
namespace dev |
namespace dev |
||||
{ |
{ |
||||
namespace eth |
namespace eth |
||||
{ |
{ |
||||
namespace jit |
namespace jit |
||||
{ |
{ |
||||
|
|
||||
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): |
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): |
||||
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
|
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
|
||||
m_gasMeter(_gasMeter) |
m_gasMeter(_gasMeter) |
||||
{ |
{ |
||||
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr}; |
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()); |
m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule()); |
||||
llvm::AttrBuilder attrBuilder; |
llvm::AttrBuilder attrBuilder; |
||||
attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly); |
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_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder)); |
||||
|
|
||||
m_require = createRequireFunc(_gasMeter); |
m_require = createRequireFunc(_gasMeter); |
||||
m_loadWord = createFunc(false, Type::Word, _gasMeter); |
m_loadWord = createFunc(false, Type::Word, _gasMeter); |
||||
m_storeWord = createFunc(true, Type::Word, _gasMeter); |
m_storeWord = createFunc(true, Type::Word, _gasMeter); |
||||
m_storeByte = createFunc(true, Type::Byte, _gasMeter); |
m_storeByte = createFunc(true, Type::Byte, _gasMeter); |
||||
} |
} |
||||
|
|
||||
llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter) |
llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter) |
||||
{ |
{ |
||||
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word}; |
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 func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); |
||||
auto rt = func->arg_begin(); |
auto rt = func->arg_begin(); |
||||
rt->setName("rt"); |
rt->setName("rt"); |
||||
auto offset = rt->getNextNode(); |
auto offset = rt->getNextNode(); |
||||
offset->setName("offset"); |
offset->setName("offset"); |
||||
auto size = offset->getNextNode(); |
auto size = offset->getNextNode(); |
||||
size->setName("size"); |
size->setName("size"); |
||||
|
|
||||
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); |
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); |
||||
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); |
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); |
||||
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); |
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); |
||||
auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); |
auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); |
||||
|
|
||||
InsertPointGuard guard(m_builder); // Restores insert point at function exit
|
InsertPointGuard guard(m_builder); // Restores insert point at function exit
|
||||
|
|
||||
// BB "Pre": Ignore checks with size 0
|
// BB "Pre": Ignore checks with size 0
|
||||
m_builder.SetInsertPoint(preBB); |
m_builder.SetInsertPoint(preBB); |
||||
auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0)); |
auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0)); |
||||
m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB); |
m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB); |
||||
|
|
||||
// BB "Check"
|
// BB "Check"
|
||||
m_builder.SetInsertPoint(checkBB); |
m_builder.SetInsertPoint(checkBB); |
||||
auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word); |
auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word); |
||||
auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res"); |
auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res"); |
||||
auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq"); |
auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq"); |
||||
auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1"); |
auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1"); |
||||
auto rtPtr = getRuntimeManager().getRuntimePtr(); |
auto rtPtr = getRuntimeManager().getRuntimePtr(); |
||||
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); |
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); |
||||
auto currSize = m_builder.CreateLoad(sizePtr, "currSize"); |
auto currSize = m_builder.CreateLoad(sizePtr, "currSize"); |
||||
auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall"); |
auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall"); |
||||
auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded"); |
auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded"); |
||||
m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights?
|
m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights?
|
||||
|
|
||||
// BB "Resize"
|
// BB "Resize"
|
||||
m_builder.SetInsertPoint(resizeBB); |
m_builder.SetInsertPoint(resizeBB); |
||||
// Check gas first
|
// Check gas first
|
||||
uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res"); |
uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res"); |
||||
auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0); |
auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0); |
||||
auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2"); |
auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2"); |
||||
auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow"); |
auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow"); |
||||
wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired); |
wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired); |
||||
wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq"); |
wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq"); |
||||
sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq"); |
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 words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k
|
||||
auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); |
auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); |
||||
_gasMeter.countMemory(newWords); |
_gasMeter.countMemory(newWords); |
||||
// Resize
|
// Resize
|
||||
m_builder.CreateStore(sizeRequired, sizePtr); |
m_builder.CreateStore(sizeRequired, sizePtr); |
||||
auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData"); |
auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData"); |
||||
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); |
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); |
||||
m_builder.CreateStore(newData, dataPtr); |
m_builder.CreateStore(newData, dataPtr); |
||||
m_builder.CreateBr(returnBB); |
m_builder.CreateBr(returnBB); |
||||
|
|
||||
// BB "Return"
|
// BB "Return"
|
||||
m_builder.SetInsertPoint(returnBB); |
m_builder.SetInsertPoint(returnBB); |
||||
m_builder.CreateRetVoid(); |
m_builder.CreateRetVoid(); |
||||
return func; |
return func; |
||||
} |
} |
||||
|
|
||||
llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&) |
llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&) |
||||
{ |
{ |
||||
auto isWord = _valueType == Type::Word; |
auto isWord = _valueType == Type::Word; |
||||
|
|
||||
llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType}; |
llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType}; |
||||
llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word}; |
llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word}; |
||||
auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; |
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 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()); |
auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule()); |
||||
|
|
||||
InsertPointGuard guard(m_builder); // Restores insert point at function exit
|
InsertPointGuard guard(m_builder); // Restores insert point at function exit
|
||||
|
|
||||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); |
m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); |
||||
auto rt = func->arg_begin(); |
auto rt = func->arg_begin(); |
||||
rt->setName("rt"); |
rt->setName("rt"); |
||||
auto index = rt->getNextNode(); |
auto index = rt->getNextNode(); |
||||
index->setName("index"); |
index->setName("index"); |
||||
|
|
||||
auto valueSize = _valueType->getPrimitiveSizeInBits() / 8; |
auto valueSize = _valueType->getPrimitiveSizeInBits() / 8; |
||||
this->require(index, Constant::get(valueSize)); |
this->require(index, Constant::get(valueSize)); |
||||
auto ptr = getBytePtr(index); |
auto ptr = getBytePtr(index); |
||||
if (isWord) |
if (isWord) |
||||
ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr"); |
ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr"); |
||||
if (_isStore) |
if (_isStore) |
||||
{ |
{ |
||||
llvm::Value* value = index->getNextNode(); |
llvm::Value* value = index->getNextNode(); |
||||
value->setName("value"); |
value->setName("value"); |
||||
if (isWord) |
if (isWord) |
||||
value = Endianness::toBE(m_builder, value); |
value = Endianness::toBE(m_builder, value); |
||||
m_builder.CreateStore(value, ptr); |
m_builder.CreateStore(value, ptr); |
||||
m_builder.CreateRetVoid(); |
m_builder.CreateRetVoid(); |
||||
} |
} |
||||
else |
else |
||||
{ |
{ |
||||
llvm::Value* ret = m_builder.CreateLoad(ptr); |
llvm::Value* ret = m_builder.CreateLoad(ptr); |
||||
ret = Endianness::toNative(m_builder, ret); |
ret = Endianness::toNative(m_builder, ret); |
||||
m_builder.CreateRet(ret); |
m_builder.CreateRet(ret); |
||||
} |
} |
||||
|
|
||||
return func; |
return func; |
||||
} |
} |
||||
|
|
||||
|
|
||||
llvm::Value* Memory::loadWord(llvm::Value* _addr) |
llvm::Value* Memory::loadWord(llvm::Value* _addr) |
||||
{ |
{ |
||||
return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr}); |
return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr}); |
||||
} |
} |
||||
|
|
||||
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) |
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) |
||||
{ |
{ |
||||
createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word}); |
createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word}); |
||||
} |
} |
||||
|
|
||||
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) |
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) |
||||
{ |
{ |
||||
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); |
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); |
||||
createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte}); |
createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte}); |
||||
} |
} |
||||
|
|
||||
llvm::Value* Memory::getData() |
llvm::Value* Memory::getData() |
||||
{ |
{ |
||||
auto rtPtr = getRuntimeManager().getRuntimePtr(); |
auto rtPtr = getRuntimeManager().getRuntimePtr(); |
||||
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); |
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); |
||||
return m_builder.CreateLoad(dataPtr, "data"); |
return m_builder.CreateLoad(dataPtr, "data"); |
||||
} |
} |
||||
|
|
||||
llvm::Value* Memory::getSize() |
llvm::Value* Memory::getSize() |
||||
{ |
{ |
||||
auto rtPtr = getRuntimeManager().getRuntimePtr(); |
auto rtPtr = getRuntimeManager().getRuntimePtr(); |
||||
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); |
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); |
||||
return m_builder.CreateLoad(sizePtr, "size"); |
return m_builder.CreateLoad(sizePtr, "size"); |
||||
} |
} |
||||
|
|
||||
llvm::Value* Memory::getBytePtr(llvm::Value* _index) |
llvm::Value* Memory::getBytePtr(llvm::Value* _index) |
||||
{ |
{ |
||||
return m_builder.CreateGEP(getData(), _index, "ptr"); |
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) |
|
||||
{ |
void Memory::require(llvm::Value* _offset, llvm::Value* _size) |
||||
createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _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) |
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, |
||||
{ |
llvm::Value* _destMemIdx, llvm::Value* _reqBytes) |
||||
require(_destMemIdx, _reqBytes); |
{ |
||||
|
require(_destMemIdx, _reqBytes); |
||||
// Additional copy cost
|
|
||||
// TODO: This round ups to 32 happens in many places
|
// Additional copy cost
|
||||
auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32)); |
// TODO: This round ups to 32 happens in many places
|
||||
m_gasMeter.countCopy(copyWords); |
auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32)); |
||||
|
m_gasMeter.countCopy(copyWords); |
||||
// Algorithm:
|
|
||||
// isOutsideData = idx256 >= size256
|
// Algorithm:
|
||||
// idx64 = trunc idx256
|
// isOutsideData = idx256 >= size256
|
||||
// size64 = trunc size256
|
// idx64 = trunc idx256
|
||||
// dataLeftSize = size64 - idx64 // safe if not isOutsideData
|
// size64 = trunc size256
|
||||
// reqBytes64 = trunc _reqBytes // require() handles large values
|
// dataLeftSize = size64 - idx64 // safe if not isOutsideData
|
||||
// bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min
|
// reqBytes64 = trunc _reqBytes // require() handles large values
|
||||
// bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
|
// 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 isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); |
||||
auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision); |
auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision); |
||||
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); |
auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision); |
||||
auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision); |
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); |
||||
auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize); |
auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision); |
||||
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64); |
auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize); |
||||
auto zero64 = llvm::ConstantInt::get(Type::lowPrecision, 0); // TODO: Cache common constants
|
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64); |
||||
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, zero64, bytesToCopyInner); |
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"); |
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); |
||||
m_builder.CreateMemCpy(dst, src, bytesToCopy, 0); |
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; |
extern "C" |
||||
|
{ |
||||
EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR
|
using namespace dev::eth::jit; |
||||
{ |
|
||||
auto size = _size->a; // Trunc to 64-bit
|
EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR
|
||||
auto& memory = _rt->getMemory(); |
{ |
||||
memory.resize(size); |
auto size = _size->a; // Trunc to 64-bit
|
||||
return memory.data(); |
auto& memory = _rt->getMemory(); |
||||
} |
memory.resize(size); |
||||
} |
return memory.data(); |
||||
|
} |
||||
|
} |
||||
|
@ -1,59 +1,46 @@ |
|||||
|
|
||||
#pragma once |
#pragma once |
||||
|
|
||||
#include <csetjmp> |
#include <csetjmp> |
||||
#include <vector> |
#include "RuntimeData.h" |
||||
|
|
||||
#include "Instruction.h" |
namespace dev |
||||
#include "CompilerHelper.h" |
{ |
||||
#include "Utils.h" |
namespace eth |
||||
#include "Type.h" |
{ |
||||
#include "RuntimeData.h" |
namespace jit |
||||
|
{ |
||||
|
|
||||
#ifdef _MSC_VER |
using StackImpl = std::vector<i256>; |
||||
#define EXPORT __declspec(dllexport) |
using MemoryImpl = bytes; |
||||
#else |
using jmp_buf_ref = decltype(&std::jmp_buf{}[0]); |
||||
#define EXPORT |
|
||||
#endif |
class Runtime |
||||
|
{ |
||||
namespace dev |
public: |
||||
{ |
Runtime(RuntimeData* _data, Env* _env); |
||||
namespace eth |
|
||||
{ |
Runtime(const Runtime&) = delete; |
||||
namespace jit |
Runtime& operator=(const Runtime&) = delete; |
||||
{ |
|
||||
|
StackImpl& getStack() { return m_stack; } |
||||
using StackImpl = std::vector<i256>; |
MemoryImpl& getMemory() { return m_memory; } |
||||
using MemoryImpl = bytes; |
Env* getEnvPtr() { return &m_env; } |
||||
using jmp_buf_ref = decltype(&std::jmp_buf{}[0]); |
|
||||
|
bytes_ref getReturnData() const; |
||||
class Runtime |
jmp_buf_ref getJmpBuf() { return m_jmpBuf; } |
||||
{ |
|
||||
public: |
private: |
||||
Runtime(RuntimeData* _data, Env* _env); |
RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract.
|
||||
|
Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract.
|
||||
Runtime(const Runtime&) = delete; |
jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract.
|
||||
Runtime& operator=(const Runtime&) = delete; |
byte* m_memoryData = nullptr; |
||||
|
i256 m_memorySize; |
||||
StackImpl& getStack() { return m_stack; } |
std::jmp_buf m_jmpBuf; |
||||
MemoryImpl& getMemory() { return m_memory; } |
StackImpl m_stack; |
||||
Env* getEnvPtr() { return &m_env; } |
MemoryImpl m_memory; |
||||
|
}; |
||||
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; |
|
||||
}; |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
@ -1,34 +1,41 @@ |
|||||
#include "interface.h" |
|
||||
#include <cstdio> |
|
||||
#include "ExecutionEngine.h" |
#include "ExecutionEngine.h" |
||||
|
|
||||
extern "C" |
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; |
EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept |
||||
auto codeSize = data->elems[RuntimeData::CodeSize].a; |
{ |
||||
bytes bytecode; |
try |
||||
bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize); |
{ |
||||
|
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)); |
auto returnCode = _engine->run(bytecode, _data, _env); |
||||
evmjit_result result = {static_cast<int32_t>(returnCode), 0, nullptr}; |
return static_cast<int>(returnCode); |
||||
if (returnCode == ReturnCode::Return && !engine.returnData.empty()) |
} |
||||
|
catch(...) |
||||
{ |
{ |
||||
// TODO: Optimized returning data. Allocating memory on client side by callback function might be a good idea
|
return static_cast<int>(ReturnCode::UnexpectedException); |
||||
result.returnDataSize = engine.returnData.size(); |
|
||||
result.returnData = std::malloc(result.returnDataSize); |
|
||||
std::memcpy(result.returnData, engine.returnData.data(), result.returnDataSize); |
|
||||
} |
} |
||||
|
|
||||
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
|
#!/usr/bin/env node
|
||||
|
|
||||
require('es6-promise').polyfill(); |
|
||||
|
|
||||
var web3 = require("../index.js"); |
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'); |
var web3 = require('./lib/web3'); |
||||
web3.providers.WebSocketProvider = require('./lib/websocket'); |
var ProviderManager = require('./lib/providermanager'); |
||||
web3.providers.HttpRpcProvider = require('./lib/httprpc'); |
web3.provider = new ProviderManager(); |
||||
web3.providers.QtProvider = require('./lib/qt'); |
web3.filter = require('./lib/filter'); |
||||
web3.providers.AutoProvider = require('./lib/autoprovider'); |
web3.providers.HttpSyncProvider = require('./lib/httpsync'); |
||||
web3.contract = require('./lib/contract'); |
web3.providers.QtSyncProvider = require('./lib/qtsync'); |
||||
|
web3.eth.contract = require('./lib/contract'); |
||||
|
web3.abi = require('./lib/abi'); |
||||
|
|
||||
|
|
||||
module.exports = web3; |
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