Browse Source

Merge branch 'develop' of github.com:ethereum/cpp-ethereum into develop

cl-refactor
Gav Wood 10 years ago
parent
commit
c72c965ea8
  1. 37
      libethereum/BlockChainSync.cpp
  2. 81
      libethereum/BlockChainSync.h
  3. 12
      mix/CodeModel.cpp
  4. 2
      mix/CodeModel.h
  5. 106
      mix/ContractCallDataEncoder.cpp
  6. 5
      mix/ContractCallDataEncoder.h
  7. 1
      mix/QVariableDeclaration.h
  8. 1
      mix/SolidityType.h
  9. 3
      mix/qml/QIntTypeView.qml
  10. 22
      mix/qml/QStringTypeView.qml
  11. 2
      mix/qml/StructView.qml
  12. 113
      mix/qml/js/InputValidator.js
  13. 9
      mix/qml/js/ProjectModel.js
  14. 28
      test/libsolidity/SolidityEndToEndTest.cpp
  15. 18
      test/libsolidity/solidityExecutionFramework.h

37
libethereum/BlockChainSync.cpp

@ -52,6 +52,7 @@ BlockChainSync::BlockChainSync(EthereumHost& _host):
BlockChainSync::~BlockChainSync() BlockChainSync::~BlockChainSync()
{ {
RecursiveGuard l(x_sync);
abortSync(); abortSync();
} }
@ -67,8 +68,10 @@ DownloadMan& BlockChainSync::downloadMan()
void BlockChainSync::abortSync() void BlockChainSync::abortSync()
{ {
DEV_INVARIANT_CHECK;
host().foreachPeer([this](EthereumPeer* _p) { onPeerAborting(_p); return true; }); host().foreachPeer([this](EthereumPeer* _p) { onPeerAborting(_p); return true; });
downloadMan().resetToChain(h256s()); downloadMan().resetToChain(h256s());
DEV_INVARIANT_CHECK;
} }
void BlockChainSync::onPeerStatus(EthereumPeer* _peer) void BlockChainSync::onPeerStatus(EthereumPeer* _peer)
@ -87,14 +90,14 @@ void BlockChainSync::onPeerStatus(EthereumPeer* _peer)
_peer->disable("Peer banned for previous bad behaviour."); _peer->disable("Peer banned for previous bad behaviour.");
else else
{ {
unsigned estimatedHashes = estimateHashes(); unsigned hashes = estimatedHashes();
_peer->m_expectedHashes = estimatedHashes; _peer->m_expectedHashes = hashes;
onNewPeer(_peer); onNewPeer(_peer);
} }
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
} }
unsigned BlockChainSync::estimateHashes() const unsigned BlockChainSync::estimatedHashes() const
{ {
BlockInfo block = host().chain().info(); BlockInfo block = host().chain().info();
time_t lastBlockTime = (block.hash() == host().chain().genesisHash()) ? 1428192000 : (time_t)block.timestamp; time_t lastBlockTime = (block.hash() == host().chain().genesisHash()) ? 1428192000 : (time_t)block.timestamp;
@ -113,7 +116,6 @@ void BlockChainSync::requestBlocks(EthereumPeer* _peer)
if (host().bq().knownFull()) if (host().bq().knownFull())
{ {
clog(NetAllDetail) << "Waiting for block queue before downloading blocks"; clog(NetAllDetail) << "Waiting for block queue before downloading blocks";
m_lastActiveState = m_state;
pauseSync(); pauseSync();
_peer->setIdle(); _peer->setIdle();
return; return;
@ -233,10 +235,8 @@ void BlockChainSync::onPeerBlocks(EthereumPeer* _peer, RLP const& _r)
{ {
if (downloadMan().isComplete()) if (downloadMan().isComplete())
completeSync(); completeSync();
else if (!got)
requestBlocks(_peer);
else else
peerDoneBlocks(_peer); requestBlocks(_peer); // Some of the blocks might have been downloaded by helping peers, proceed anyway
} }
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
} }
@ -508,7 +508,7 @@ bool PV60Sync::shouldGrabBlocks(EthereumPeer* _peer) const
{ {
auto td = _peer->m_totalDifficulty; auto td = _peer->m_totalDifficulty;
auto lh = _peer->m_latestHash; auto lh = _peer->m_latestHash;
auto ctd = host().chain().details().totalDifficulty; auto ctd = host().chain().details().totalDifficulty;
if (m_syncingNeededBlocks.empty()) if (m_syncingNeededBlocks.empty())
return false; return false;
@ -636,7 +636,9 @@ void PV60Sync::noteDoneBlocks(EthereumPeer* _peer, bool _clemency)
// m_banned.insert(_peer->session()->id()); // We know who you are! // m_banned.insert(_peer->session()->id()); // We know who you are!
// _peer->disable("Peer sent hashes but was unable to provide the blocks."); // _peer->disable("Peer sent hashes but was unable to provide the blocks.");
} }
resetSync();
downloadMan().reset(); downloadMan().reset();
transition(_peer, SyncState::Idle);
} }
_peer->m_sub.doneFetch(); _peer->m_sub.doneFetch();
} }
@ -648,7 +650,7 @@ void PV60Sync::onPeerHashes(EthereumPeer* _peer, h256s const& _hashes)
_peer->setIdle(); _peer->setIdle();
if (!isSyncing(_peer)) if (!isSyncing(_peer))
{ {
clog(NetMessageSummary) << "Ignoring hashes synce not syncing"; clog(NetMessageSummary) << "Ignoring hashes since not syncing";
return; return;
} }
if (_hashes.size() == 0) if (_hashes.size() == 0)
@ -705,6 +707,7 @@ void PV60Sync::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
clog(NetMessageSummary) << "Ignoring since we're already downloading."; clog(NetMessageSummary) << "Ignoring since we're already downloading.";
return; return;
} }
clog(NetMessageDetail) << "Not syncing and new block hash discovered: syncing without help.";
unsigned knowns = 0; unsigned knowns = 0;
unsigned unknowns = 0; unsigned unknowns = 0;
for (auto const& h: _hashes) for (auto const& h: _hashes)
@ -741,6 +744,7 @@ void PV60Sync::onPeerNewHashes(EthereumPeer* _peer, h256s const& _hashes)
void PV60Sync::abortSync(EthereumPeer* _peer) void PV60Sync::abortSync(EthereumPeer* _peer)
{ {
// Can't check invariants here since the peers is already removed from the list and the state is not updated yet.
if (isSyncing(_peer)) if (isSyncing(_peer))
{ {
host().foreachPeer([this](EthereumPeer* _p) { _p->setIdle(); return true; }); host().foreachPeer([this](EthereumPeer* _p) { _p->setIdle(); return true; });
@ -751,6 +755,8 @@ void PV60Sync::abortSync(EthereumPeer* _peer)
void PV60Sync::onPeerAborting(EthereumPeer* _peer) void PV60Sync::onPeerAborting(EthereumPeer* _peer)
{ {
RecursiveGuard l(x_sync);
// Can't check invariants here since the peers is already removed from the list and the state is not updated yet.
abortSync(_peer); abortSync(_peer);
DEV_INVARIANT_CHECK; DEV_INVARIANT_CHECK;
} }
@ -767,6 +773,10 @@ bool PV60Sync::invariants() const
host().foreachPeer([&](EthereumPeer* _p) { if (_p->m_asking == Asking::Hashes) hashes = true; return !hashes; }); host().foreachPeer([&](EthereumPeer* _p) { if (_p->m_asking == Asking::Hashes) hashes = true; return !hashes; });
if (!hashes) if (!hashes)
return false; return false;
if (!m_syncingLatestHash)
return false;
if (m_syncingNeededBlocks.empty() != (!m_syncingLastReceivedHash))
return false;
} }
if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks) if (m_state == SyncState::Blocks || m_state == SyncState::NewBlocks)
{ {
@ -777,5 +787,14 @@ bool PV60Sync::invariants() const
if (downloadMan().isComplete()) if (downloadMan().isComplete())
return false; return false;
} }
if (m_state == SyncState::Idle)
{
bool busy = false;
host().foreachPeer([&](EthereumPeer* _p) { if (_p->m_asking != Asking::Nothing && _p->m_asking != Asking::State) busy = true; return !busy; });
if (busy)
return false;
}
if (m_state == SyncState::Waiting && !host().bq().isActive())
return false;
return true; return true;
} }

81
libethereum/BlockChainSync.h

@ -109,17 +109,16 @@ protected:
EthereumHost const& host() const { return m_host; } EthereumHost const& host() const { return m_host; }
/// Estimates max number of hashes peers can give us. /// Estimates max number of hashes peers can give us.
unsigned estimateHashes() const; unsigned estimatedHashes() const;
/// Request blocks from peer if needed /// Request blocks from peer if needed
void requestBlocks(EthereumPeer* _peer); void requestBlocks(EthereumPeer* _peer);
protected: protected:
Handler m_bqRoomAvailable; Handler m_bqRoomAvailable; ///< Triggered once block queue
mutable RecursiveMutex x_sync; mutable RecursiveMutex x_sync;
SyncState m_state = SyncState::Idle; ///< Current sync state SyncState m_state = SyncState::Idle; ///< Current sync state
SyncState m_lastActiveState = SyncState::Idle; ///< Saved state before entering waiting queue mode unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only.
unsigned m_estimatedHashes = 0; ///< Number of estimated hashes for the last peer over PV60. Used for status reporting only.
private: private:
static char const* const s_stateNames[static_cast<int>(SyncState::Size)]; static char const* const s_stateNames[static_cast<int>(SyncState::Size)];
@ -133,6 +132,70 @@ private:
* @brief Syncrhonization over PV60. Selects a single peer and tries to downloading hashes from it. After hash downaload is complete * @brief Syncrhonization over PV60. Selects a single peer and tries to downloading hashes from it. After hash downaload is complete
* Syncs to peers and keeps up to date * Syncs to peers and keeps up to date
*/ */
/**
* Transitions:
*
* Idle->Hashes
* Triggered when:
* * A new peer appears that we can sync to
* * Transtition to Idle, there are peers we can sync to
* Effects:
* * Set chain sync (m_syncingTotalDifficulty, m_syncingLatestHash, m_syncer)
* * Requests hashes from m_syncer
*
* Hashes->Idle
* Triggered when:
* * Received too many hashes
* * Received 0 total hashes from m_syncer
* * m_syncer aborts
* Effects:
* In case of too many hashes sync is reset
*
* Hashes->Blocks
* Triggered when:
* * Received known hash from m_syncer
* * Received 0 hashes from m_syncer and m_syncingTotalBlocks not empty
* Effects:
* * Set up download manager, clear m_syncingTotalBlocks. Set all peers to help with downloading if they can
*
* Blocks->Idle
* Triggered when:
* * m_syncer aborts
* * m_syncer does not have required block
* * All blocks downloaded
* * Block qeueue is full with unknown blocks
* Effects:
* * Download manager is reset
*
* Blocks->Waiting
* Triggered when:
* * Block queue is full with known blocks
* Effects:
* * Stop requesting blocks from peers
*
* Waiting->Blocks
* Triggered when:
* * Block queue has space for new blocks
* Effects:
* * Continue requesting blocks from peers
*
* Idle->NewBlocks
* Triggered when:
* * New block hashes arrive
* Effects:
* * Set up download manager, clear m_syncingTotalBlocks. Download blocks from a single peer. If downloaded blocks have unknown parents, set the peer to sync
*
* NewBlocks->Idle
* Triggered when:
* * m_syncer aborts
* * m_syncer does not have required block
* * All new blocks downloaded
* * Block qeueue is full with unknown blocks
* Effects:
* * Download manager is reset
*
*/
class PV60Sync: public BlockChainSync class PV60Sync: public BlockChainSync
{ {
public: public:
@ -153,6 +216,7 @@ public:
/// @returns Sync status /// @returns Sync status
SyncStatus status() const override; SyncStatus status() const override;
protected:
void onNewPeer(EthereumPeer* _peer) override; void onNewPeer(EthereumPeer* _peer) override;
void continueSync() override; void continueSync() override;
void peerDoneBlocks(EthereumPeer* _peer) override; void peerDoneBlocks(EthereumPeer* _peer) override;
@ -205,9 +269,10 @@ private:
h256s m_syncingNeededBlocks; ///< The blocks that we should download from this peer. h256s m_syncingNeededBlocks; ///< The blocks that we should download from this peer.
h256 m_syncingLastReceivedHash; ///< Hash most recently received from peer. h256 m_syncingLastReceivedHash; ///< Hash most recently received from peer.
h256 m_syncingLatestHash; ///< Peer's latest block's hash, as of the current sync. h256 m_syncingLatestHash; ///< Latest block's hash of the peer we are syncing to, as of the current sync.
u256 m_syncingTotalDifficulty; ///< Peer's latest block's total difficulty, as of the current sync. u256 m_syncingTotalDifficulty; ///< Latest block's total difficulty of the peer we aresyncing to, as of the current sync.
EthereumPeer* m_syncer = nullptr; // TODO: switch to weak_ptr // TODO: switch to weak_ptr
EthereumPeer* m_syncer = nullptr; ///< Peer we are currently syncing with
}; };
} }
} }

12
mix/CodeModel.cpp

@ -493,9 +493,18 @@ dev::bytes const& CodeModel::getStdContractCode(const QString& _contractName, co
return m_compiledContracts.at(_contractName); return m_compiledContracts.at(_contractName);
} }
void CodeModel::retrieveSubType(SolidityType& _wrapperType, dev::solidity::Type const* _type)
{
if (_type->getCategory() == Type::Category::Array)
{
ArrayType const* arrayType = dynamic_cast<ArrayType const*>(_type);
_wrapperType.baseType = std::make_shared<dev::mix::SolidityType const>(nodeType(arrayType->getBaseType().get()));
}
}
SolidityType CodeModel::nodeType(dev::solidity::Type const* _type) SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
{ {
SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString()), std::vector<SolidityDeclaration>(), std::vector<QString>() }; SolidityType r { SolidityType::Type::UnsignedInteger, 32, 1, false, false, QString::fromStdString(_type->toString()), std::vector<SolidityDeclaration>(), std::vector<QString>(), nullptr };
if (!_type) if (!_type)
return r; return r;
switch (_type->getCategory()) switch (_type->getCategory())
@ -536,6 +545,7 @@ SolidityType CodeModel::nodeType(dev::solidity::Type const* _type)
r.count = static_cast<unsigned>(array->getLength()); r.count = static_cast<unsigned>(array->getLength());
r.dynamicSize = _type->isDynamicallySized(); r.dynamicSize = _type->isDynamicallySized();
r.array = true; r.array = true;
retrieveSubType(r, _type);
} }
break; break;
case Type::Category::Enum: case Type::Category::Enum:

2
mix/CodeModel.h

@ -224,6 +224,8 @@ public:
Q_INVOKABLE void unregisterContractSrc(QString const& _documentId); Q_INVOKABLE void unregisterContractSrc(QString const& _documentId);
/// Convert solidity type info to mix type /// Convert solidity type info to mix type
static SolidityType nodeType(dev::solidity::Type const* _type); static SolidityType nodeType(dev::solidity::Type const* _type);
/// Retrieve subtype
static void retrieveSubType(SolidityType& _wrapperType, dev::solidity::Type const* _type);
/// Check if given location belongs to contract or function /// Check if given location belongs to contract or function
bool isContractOrFunctionLocation(dev::SourceLocation const& _location); bool isContractOrFunctionLocation(dev::SourceLocation const& _location);
/// Get funciton name by location /// Get funciton name by location

106
mix/ContractCallDataEncoder.cpp

@ -20,9 +20,12 @@
* Ethereum IDE client. * Ethereum IDE client.
*/ */
#include <QDebug> #include <vector>
#include <QMap> #include <QMap>
#include <QStringList> #include <QStringList>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <libethcore/CommonJS.h> #include <libethcore/CommonJS.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include "QVariableDeclaration.h" #include "QVariableDeclaration.h"
@ -39,13 +42,18 @@ bytes ContractCallDataEncoder::encodedData()
bytes r(m_encodedData); bytes r(m_encodedData);
size_t headerSize = m_encodedData.size() & ~0x1fUL; //ignore any prefix that is not 32-byte aligned size_t headerSize = m_encodedData.size() & ~0x1fUL; //ignore any prefix that is not 32-byte aligned
//apply offsets //apply offsets
for (auto const& p: m_offsetMap) for (auto const& p: m_dynamicOffsetMap)
{
vector_ref<byte> offsetRef(m_dynamicData.data() + p.first, 32);
toBigEndian(p.second + headerSize, offsetRef); //add header size minus signature hash
}
for (auto const& p: m_staticOffsetMap)
{ {
vector_ref<byte> offsetRef(r.data() + p.first, 32); vector_ref<byte> offsetRef(r.data() + p.first, 32);
toBigEndian<size_t, vector_ref<byte>>(p.second + headerSize, offsetRef); //add header size minus signature hash toBigEndian(p.second + headerSize, offsetRef); //add header size minus signature hash
} }
if (m_dynamicData.size() > 0)
r.insert(r.end(), m_dynamicData.begin(), m_dynamicData.end()); r.insert(r.end(), m_dynamicData.begin(), m_dynamicData.end());
return r; return r;
} }
@ -55,54 +63,69 @@ void ContractCallDataEncoder::encode(QFunctionDefinition const* _function)
m_encodedData.insert(m_encodedData.end(), hash.begin(), hash.end()); m_encodedData.insert(m_encodedData.end(), hash.begin(), hash.end());
} }
void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& _type) void ContractCallDataEncoder::encodeArray(QJsonArray const& _array, SolidityType const& _type, bytes& _content)
{ {
u256 count = 1; size_t offsetStart = _content.size();
QStringList strList; if (_type.dynamicSize)
if (_type.array)
{ {
if (_data.type() == QVariant::String) bytes count = bytes(32);
strList = _data.toString().split(",", QString::SkipEmptyParts); //TODO: proper parsing toBigEndian((u256)_array.size(), count);
else _content += count; //reserved space for count
strList = _data.toStringList(); }
count = strList.count();
int k = 0;
for (QJsonValue const& c: _array)
{
if (c.isArray())
{
if (_type.baseType->dynamicSize)
m_dynamicOffsetMap.push_back(std::make_pair(m_dynamicData.size() + offsetStart + 32 + k * 32, m_dynamicData.size() + _content.size()));
encodeArray(c.toArray(), *_type.baseType, _content);
}
else
{
// encode single item
if (c.isDouble())
encodeSingleItem(QString::number(c.toDouble()), _type, _content);
else if (c.isString())
encodeSingleItem(c.toString(), _type, _content);
}
k++;
} }
else }
strList.append(_data.toString());
if (_type.dynamicSize) void ContractCallDataEncoder::encode(QVariant const& _data, SolidityType const& _type)
{
if (_type.dynamicSize && (_type.type == SolidityType::Type::Bytes || _type.type == SolidityType::Type::String))
{ {
bytes empty(32); bytes empty(32);
size_t sizePos = m_dynamicData.size(); size_t sizePos = m_dynamicData.size();
m_dynamicData += empty; //reserve space for count m_dynamicData += empty; //reserve space for count
if (_type.type == SolidityType::Type::Bytes) u256 count = encodeSingleItem(_data.toString(), _type, m_dynamicData);
count = encodeSingleItem(_data.toString(), _type, m_dynamicData);
else
{
count = strList.count();
for (auto const& item: strList)
encodeSingleItem(item, _type, m_dynamicData);
}
vector_ref<byte> sizeRef(m_dynamicData.data() + sizePos, 32); vector_ref<byte> sizeRef(m_dynamicData.data() + sizePos, 32);
toBigEndian(count, sizeRef); toBigEndian(count, sizeRef);
m_offsetMap.push_back(std::make_pair(m_encodedData.size(), sizePos)); m_staticOffsetMap.push_back(std::make_pair(m_encodedData.size(), sizePos));
m_encodedData += empty; //reserve space for offset m_encodedData += empty; //reserve space for offset
} }
else else if (_type.array)
{ {
if (_type.array) bytes content;
count = _type.count; size_t size = m_encodedData.size();
int c = static_cast<int>(count); if (_type.dynamicSize)
if (strList.size() > c) {
strList.erase(strList.begin() + c, strList.end()); m_encodedData += bytes(32); // reserve space for offset
else m_staticOffsetMap.push_back(std::make_pair(size, m_dynamicData.size()));
while (strList.size() < c) }
strList.append(QString()); QJsonDocument jsonDoc = QJsonDocument::fromJson(_data.toString().toUtf8());
encodeArray(jsonDoc.array(), _type, content);
for (auto const& item: strList) if (!_type.dynamicSize)
encodeSingleItem(item, _type, m_encodedData); m_encodedData.insert(m_encodedData.end(), content.begin(), content.end());
else
m_dynamicData.insert(m_dynamicData.end(), content.begin(), content.end());
} }
else
encodeSingleItem(_data.toString(), _type, m_encodedData);
} }
unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, SolidityType const& _type, bytes& _dest) unsigned ContractCallDataEncoder::encodeSingleItem(QString const& _data, SolidityType const& _type, bytes& _dest)
@ -207,6 +230,13 @@ QString ContractCallDataEncoder::toString(dev::bytes const& _b)
return QString::fromStdString(dev::toJS(_b)); return QString::fromStdString(dev::toJS(_b));
} }
QString ContractCallDataEncoder::toChar(dev::bytes const& _b)
{
QString str;
asString(_b, str);
return str;
}
QVariant ContractCallDataEncoder::decode(SolidityType const& _type, bytes const& _value) QVariant ContractCallDataEncoder::decode(SolidityType const& _type, bytes const& _value)
{ {
@ -220,6 +250,8 @@ QVariant ContractCallDataEncoder::decode(SolidityType const& _type, bytes const&
return QVariant::fromValue(toString(decodeBool(rawParam))); return QVariant::fromValue(toString(decodeBool(rawParam)));
else if (type == QSolidityType::Type::Bytes || type == QSolidityType::Type::Hash) else if (type == QSolidityType::Type::Bytes || type == QSolidityType::Type::Hash)
return QVariant::fromValue(toString(decodeBytes(rawParam))); return QVariant::fromValue(toString(decodeBytes(rawParam)));
else if (type == QSolidityType::Type::String)
return QVariant::fromValue(toChar(decodeBytes(rawParam)));
else if (type == QSolidityType::Type::Struct) else if (type == QSolidityType::Type::Struct)
return QVariant::fromValue(QString("struct")); //TODO return QVariant::fromValue(QString("struct")); //TODO
else if (type == QSolidityType::Type::Address) else if (type == QSolidityType::Type::Address)

5
mix/ContractCallDataEncoder.h

@ -71,11 +71,14 @@ private:
QString toString(bool _b); QString toString(bool _b);
QString toString(dev::bytes const& _b); QString toString(dev::bytes const& _b);
bool asString(dev::bytes const& _b, QString& _str); bool asString(dev::bytes const& _b, QString& _str);
void encodeArray(QJsonArray const& _array, SolidityType const& _type, bytes& _content);
QString toChar(dev::bytes const& _b);
private: private:
bytes m_encodedData; bytes m_encodedData;
bytes m_dynamicData; bytes m_dynamicData;
std::vector<std::pair<size_t, size_t>> m_offsetMap; std::vector<std::pair<size_t, size_t>> m_dynamicOffsetMap;
std::vector<std::pair<size_t, size_t>> m_staticOffsetMap;
}; };
} }

1
mix/QVariableDeclaration.h

@ -60,6 +60,7 @@ public:
Bool, Bool,
Address, Address,
Bytes, Bytes,
String,
Enum, Enum,
Struct Struct
}; };

1
mix/SolidityType.h

@ -57,6 +57,7 @@ struct SolidityType
QString name; QString name;
std::vector<SolidityDeclaration> members; //for struct std::vector<SolidityDeclaration> members; //for struct
std::vector<QString> enumNames; //for enum std::vector<QString> enumNames; //for enum
std::shared_ptr<SolidityType const> baseType;
}; };
struct SolidityDeclaration struct SolidityDeclaration

3
mix/qml/QIntTypeView.qml

@ -33,6 +33,3 @@ Item
} }
} }
} }

22
mix/qml/QStringTypeView.qml

@ -5,25 +5,31 @@ Item
property alias value: textinput.text property alias value: textinput.text
property alias readOnly: textinput.readOnly property alias readOnly: textinput.readOnly
id: editRoot id: editRoot
height: 20
width: readOnly ? textinput.implicitWidth : 150 width: readOnly ? textinput.implicitWidth : 150
SourceSansProBold DebuggerPaneStyle {
{ id: dbgStyle
id: boldFont
} }
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
radius: 4 radius: 4
TextInput { TextInput {
anchors.verticalCenter: parent.verticalCenter
id: textinput id: textinput
text: value font.family: dbgStyle.general.basicFont
clip: true clip: true
anchors.fill: parent
wrapMode: Text.WrapAnywhere
font.family: boldFont.name
selectByMouse: true selectByMouse: true
text: value
anchors.fill: parent
font.pointSize: dbgStyle.general.basicFontSize
color: dbgStyle.general.basicColor
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
onClicked: textinput.forceActiveFocus()
}
} }
} }
} }

2
mix/qml/StructView.qml

@ -72,7 +72,7 @@ Column
return Qt.createComponent("qrc:/qml/QIntTypeView.qml"); return Qt.createComponent("qrc:/qml/QIntTypeView.qml");
else if (t === QSolidityType.Bool) else if (t === QSolidityType.Bool)
return Qt.createComponent("qrc:/qml/QBoolTypeView.qml"); return Qt.createComponent("qrc:/qml/QBoolTypeView.qml");
else if (t === QSolidityType.Bytes) else if (t === QSolidityType.Bytes || t === QSolidityType.String)
return Qt.createComponent("qrc:/qml/QStringTypeView.qml"); return Qt.createComponent("qrc:/qml/QStringTypeView.qml");
else if (t === QSolidityType.Hash) else if (t === QSolidityType.Hash)
return Qt.createComponent("qrc:/qml/QHashTypeView.qml"); return Qt.createComponent("qrc:/qml/QHashTypeView.qml");

113
mix/qml/js/InputValidator.js

@ -1,27 +1,20 @@
Qt.include("QEtherHelper.js") Qt.include("QEtherHelper.js")
var nbRegEx = new RegExp('^[0-9]+$'); var nbRegEx;
var arrayRegEx;
var capturenbRegEx;
function validate(model, values) function validate(model, values)
{ {
var inError = []; var inError = [];
for (var k in model) for (var k in model)
{ {
init()
if (values[model[k].name]) if (values[model[k].name])
{ {
var type = model[k].type.name; var type = model[k].type.name;
var res; var value = values[model[k].name];
if (isContractType(type)) var res = check(type, value)
res = validateAddress(type, values[model[k].name]);
else if (type.indexOf("int") !== -1)
res = validateInt(type, values[model[k].name]);
else if (type.indexOf("bytes") !== -1)
res = validateBytes(type, values[model[k].name]);
else if (type.indexOf("bool") !== -1)
res = validateBool(type, values[model[k].name]);
else if (type.indexOf("address") !== -1)
res = validateAddress(type, values[model[k].name]);
else
res.valid = true;
if (!res.valid) if (!res.valid)
inError.push({ type: type, value: values, message: res.message }); inError.push({ type: type, value: values, message: res.message });
} }
@ -29,6 +22,96 @@ function validate(model, values)
return inError; return inError;
} }
function init()
{
nbRegEx = new RegExp('^[0-9]+$');
arrayRegEx = new RegExp('\\[[^\\]]*\\]', "g");
capturenbRegEx = new RegExp("[0-9]+");
}
function check(type, value)
{
var res = { valid: true, message : "" }
if (isContractType(type))
res = validateAddress(type, value);
else if (isArray(type))
res = validateArray(type, value);
else if (type.indexOf("int") !== -1)
res = validateInt(type, value);
else if (type.indexOf("bytes") !== -1)
res = validateBytes(type, value);
else if (type.indexOf("bool") !== -1)
res = validateBool(type, value);
else if (type.indexOf("address") !== -1)
res = validateAddress(type, value);
else
{
res.valid = true
res.message = ""
}
return res;
}
function isArray(_type)
{
return arrayRegEx.test(_type);
}
function checkArrayRecursively(_type, _dim, _array)
{
if (_array instanceof Array)
{
if (_dim.length === 0)
return { valid: false, message: "Your input contains too many dimensions" }
var size = -1
var infinite = false
if (_dim === "[]")
infinite = true
else
size = parseInt(capturenbRegEx.exec(_dim[0]))
if (_array.length > size && !infinite)
return { valid: false, message: "Array size does not correspond. Should be " + _dim[0] }
if (_array.length > 0)
{
var _newdim = _dim.slice(0)
_newdim.splice(0, 1)
for (var i = 0; i < _array.length; i++)
{
var ret = checkArrayRecursively(_type, _newdim, _array[i])
if (!ret.valid)
return ret
}
}
return { valid: true, message: "" }
}
else
{
if (_dim.length > 0)
return { valid: false, message: "Your input contains too few dimensions" }
if (typeof(_array) === "number")
_array = '' + _array + ''
return check(_type, _array)
}
}
function validateArray(_type, _value)
{
try
{
_value = JSON.parse(_value)
}
catch (e)
{
return { valid: false, message: "Input must be JSON formatted like [1,5,3,9] or [[4,9],[4,9],[4,9],[4,9]]" }
}
var dim = _type.match(arrayRegEx)
dim.reverse();
for (var k = 0; k < dim.length; k++)
_type = _type.replace(dim[k], "")
_type = _type.replace(/calldata/g, "")
return checkArrayRecursively(_type, dim, _value)
}
function isContractType(_type) function isContractType(_type)
{ {
for (var k in Object.keys(codeModel.contracts)) for (var k in Object.keys(codeModel.contracts))
@ -100,7 +183,7 @@ function validateAddress(_type, _value)
function validateBytes(_type, _value) function validateBytes(_type, _value)
{ {
var ret = { valid: true, message: "" } var ret = { valid: true, message: "" }
if (_value.length > parseInt(_type.replace("bytes", "")) ) if (_type !== "bytes" && _value.length > parseInt(_type.replace("bytes", "")) )
{ {
ret.valid = false; ret.valid = false;
ret.message = _type + " should not contains more than " + _type.replace("bytes", "") + " characters"; ret.message = _type + " should not contains more than " + _type.replace("bytes", "") + " characters";

9
mix/qml/js/ProjectModel.js

@ -254,8 +254,10 @@ function doCreateProject(title, path) {
files: [ contractsFile, indexFile ] files: [ contractsFile, indexFile ]
}; };
//TODO: copy from template //TODO: copy from template
fileIo.writeFile(dirPath + indexFile, htmlTemplate); if (!fileIo.fileExists(dirPath + indexFile))
fileIo.writeFile(dirPath + contractsFile, contractTemplate); fileIo.writeFile(dirPath + indexFile, htmlTemplate);
if (!fileIo.fileExists(dirPath + contractsFile))
fileIo.writeFile(dirPath + contractsFile, contractTemplate);
newProject(projectData); newProject(projectData);
var json = JSON.stringify(projectData, null, "\t"); var json = JSON.stringify(projectData, null, "\t");
fileIo.writeFile(projectFile, json); fileIo.writeFile(projectFile, json);
@ -342,7 +344,8 @@ function newContract() {
function createAndAddFile(name, extension, content) { function createAndAddFile(name, extension, content) {
var fileName = generateFileName(name, extension); var fileName = generateFileName(name, extension);
var filePath = projectPath + fileName; var filePath = projectPath + fileName;
fileIo.writeFile(filePath, content); if (!fileIo.fileExists(filePath))
fileIo.writeFile(filePath, content);
var id = addFile(fileName); var id = addFile(fileName);
saveProjectFile(); saveProjectFile();
documentAdded(id); documentAdded(id);

28
test/libsolidity/SolidityEndToEndTest.cpp

@ -1726,7 +1726,7 @@ BOOST_AUTO_TEST_CASE(fixed_bytes_in_calls)
BOOST_CHECK(callContractFunction("callHelper(bytes2,bool)", string("\0a", 2), true) == encodeArgs(string("\0a\0\0\0", 5))); BOOST_CHECK(callContractFunction("callHelper(bytes2,bool)", string("\0a", 2), true) == encodeArgs(string("\0a\0\0\0", 5)));
} }
BOOST_AUTO_TEST_CASE(constructor_arguments) BOOST_AUTO_TEST_CASE(constructor_arguments_internal)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(
contract Helper { contract Helper {
@ -1749,8 +1749,28 @@ BOOST_AUTO_TEST_CASE(constructor_arguments)
function getName() returns (bytes3 ret) { return h.getName(); } function getName() returns (bytes3 ret) { return h.getName(); }
})"; })";
compileAndRun(sourceCode, 0, "Main"); compileAndRun(sourceCode, 0, "Main");
BOOST_REQUIRE(callContractFunction("getFlag()") == encodeArgs(true)); BOOST_CHECK(callContractFunction("getFlag()") == encodeArgs(true));
BOOST_REQUIRE(callContractFunction("getName()") == encodeArgs("abc")); BOOST_CHECK(callContractFunction("getName()") == encodeArgs("abc"));
}
BOOST_AUTO_TEST_CASE(constructor_arguments_external)
{
char const* sourceCode = R"(
contract Main {
bytes3 name;
bool flag;
function Main(bytes3 x, bool f) {
name = x;
flag = f;
}
function getName() returns (bytes3 ret) { return name; }
function getFlag() returns (bool ret) { return flag; }
}
)";
compileAndRun(sourceCode, 0, "Main", encodeArgs("abc", true));
BOOST_CHECK(callContractFunction("getFlag()") == encodeArgs(true));
BOOST_CHECK(callContractFunction("getName()") == encodeArgs("abc"));
} }
BOOST_AUTO_TEST_CASE(functions_called_by_constructor) BOOST_AUTO_TEST_CASE(functions_called_by_constructor)
@ -4166,7 +4186,7 @@ BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund)
} }
} }
)"; )";
BOOST_CHECK(compileAndRunWthoutCheck(sourceCode, 0, "A").empty()); BOOST_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "A").empty());
} }
BOOST_AUTO_TEST_CASE(positive_integers_to_signed) BOOST_AUTO_TEST_CASE(positive_integers_to_signed)

18
test/libsolidity/solidityExecutionFramework.h

@ -42,19 +42,29 @@ class ExecutionFramework
public: public:
ExecutionFramework() { g_logVerbosity = 0; } ExecutionFramework() { g_logVerbosity = 0; }
bytes const& compileAndRunWthoutCheck(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") bytes const& compileAndRunWithoutCheck(
std::string const& _sourceCode,
u256 const& _value = 0,
std::string const& _contractName = "",
bytes const& _arguments = bytes()
)
{ {
m_compiler.reset(false, m_addStandardSources); m_compiler.reset(false, m_addStandardSources);
m_compiler.addSource("", _sourceCode); m_compiler.addSource("", _sourceCode);
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
bytes code = m_compiler.getBytecode(_contractName); bytes code = m_compiler.getBytecode(_contractName);
sendMessage(code, true, _value); sendMessage(code + _arguments, true, _value);
return m_output; return m_output;
} }
bytes const& compileAndRun(std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "") bytes const& compileAndRun(
std::string const& _sourceCode,
u256 const& _value = 0,
std::string const& _contractName = "",
bytes const& _arguments = bytes()
)
{ {
compileAndRunWthoutCheck(_sourceCode, _value, _contractName); compileAndRunWithoutCheck(_sourceCode, _value, _contractName, _arguments);
BOOST_REQUIRE(!m_output.empty()); BOOST_REQUIRE(!m_output.empty());
return m_output; return m_output;
} }

Loading…
Cancel
Save