You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

665 lines
15 KiB

import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.1
import Qt.labs.settings 1.0
import org.ethereum.qml.QEther 1.0
import "js/Debugger.js" as Debugger
import "js/ErrorLocationFormater.js" as ErrorLocationFormater
import "js/TransactionHelper.js" as TransactionHelper
import "js/QEtherHelper.js" as QEtherHelper
import "."
ColumnLayout {
10 years ago
id: blockChainPanel
property alias trDialog: transactionDialog
property alias blockChainRepeater: blockChainRepeater
10 years ago
property variant model
property int scenarioIndex
property var states: ({})
10 years ago
spacing: 0
property int previousWidth
property variant debugTrRequested: []
signal chainChanged(var blockIndex, var txIndex, var item)
signal chainReloaded
signal txSelected(var blockIndex, var txIndex)
signal rebuilding
10 years ago
signal accountAdded(string address, string amount)
Connections
{
target: projectModel.stateListModel
onAccountsValidated:
{
if (rebuild.accountsSha3 !== codeModel.sha3(JSON.stringify(_accounts)))
rebuild.needRebuild("AccountsChanged")
else
rebuild.notNeedRebuild("AccountsChanged")
}
onContractsValidated:
{
if (rebuild.contractsSha3 !== codeModel.sha3(JSON.stringify(_contracts)))
rebuild.needRebuild("ContractsChanged")
else
rebuild.notNeedRebuild("ContractsChanged")
}
}
Connections
{
target: codeModel
onContractRenamed: {
rebuild.needRebuild("ContractRenamed")
}
onNewContractCompiled: {
rebuild.needRebuild("NewContractCompiled")
}
onCompilationComplete: {
for (var c in rebuild.contractsHex)
{
if (codeModel.contracts[c] === undefined || codeModel.contracts[c].codeHex !== rebuild.contractsHex[c])
{
if (!rebuild.containsRebuildCause("CodeChanged"))
{
rebuild.needRebuild("CodeChanged")
}
return
}
}
rebuild.notNeedRebuild("CodeChanged")
}
}
10 years ago
10 years ago
onChainChanged: {
if (rebuild.txSha3[blockIndex][txIndex] !== codeModel.sha3(JSON.stringify(model.blocks[blockIndex].transactions[txIndex])))
{
rebuild.txChanged.push(rebuild.txSha3[blockIndex][txIndex])
rebuild.needRebuild("txChanged")
}
else {
for (var k in rebuild.txChanged)
{
if (rebuild.txChanged[k] === rebuild.txSha3[blockIndex][txIndex])
{
rebuild.txChanged.splice(k, 1)
break
}
}
if (rebuild.txChanged.length === 0)
rebuild.notNeedRebuild("txChanged")
}
10 years ago
}
onWidthChanged:
{
10 years ago
var minWidth = scenarioMinWidth - 20 // margin
if (width <= minWidth || previousWidth <= minWidth)
10 years ago
{
fromWidth = 250
toWidth = 240
10 years ago
}
else
{
var diff = (width - previousWidth) / 3;
fromWidth = fromWidth + diff < 250 ? 250 : fromWidth + diff
toWidth = toWidth + diff < 240 ? 240 : toWidth + diff
10 years ago
}
previousWidth = width
}
function getState(record)
{
return states[record]
}
function load(scenario, index)
10 years ago
{
if (!scenario)
return;
if (model)
rebuild.startBlinking()
10 years ago
model = scenario
scenarioIndex = index
genesis.scenarioIndex = index
states = []
10 years ago
blockModel.clear()
for (var b in model.blocks)
blockModel.append(model.blocks[b])
previousWidth = width
}
10 years ago
property int statusWidth: 30
property int fromWidth: 250
property int toWidth: 240
property int debugActionWidth: 40
10 years ago
property int horizontalMargin: 10
property int cellSpacing: 10
10 years ago
RowLayout
{
Layout.preferredHeight: 10
}
10 years ago
Rectangle
{
Layout.preferredHeight: 500
Layout.preferredWidth: parent.width
border.color: "#cccccc"
border.width: 2
color: "white"
ScrollView
{
id: blockChainScrollView
anchors.fill: parent
10 years ago
anchors.topMargin: 8
10 years ago
ColumnLayout
{
id: blockChainLayout
width: parent.width
10 years ago
spacing: 20
Block
{
id: genesis
scenario: blockChainPanel.model
scenarioIndex: scenarioIndex
Layout.preferredWidth: blockChainScrollView.width
Layout.preferredHeight: 60
blockIndex: -1
transactions: []
status: ""
number: -2
trHeight: 60
}
10 years ago
Repeater // List of blocks
{
id: blockChainRepeater
model: blockModel
function editTx(blockIndex, txIndex)
{
itemAt(blockIndex).editTx(txIndex)
}
function select(blockIndex, txIndex)
{
itemAt(blockIndex).select(txIndex)
}
10 years ago
Block
{
Connections
{
target: block
onTxSelected:
{
blockChainPanel.txSelected(index, txIndex)
}
}
id: block
10 years ago
scenario: blockChainPanel.model
Layout.preferredWidth: blockChainScrollView.width
Layout.preferredHeight:
{
return calculateHeight()
}
blockIndex: index
transactions:
{
if (index >= 0)
return blockModel.get(index).transactions
else
return []
}
transactionModel:
{
if (index >= 0)
return scenario.blocks[index].transactions
else
return []
}
10 years ago
status:
{
if (index >= 0)
return blockModel.get(index).status
else
return ""
}
number:
{
if (index >= 0)
return blockModel.get(index).number
else
return 0
}
}
}
}
}
}
ListModel
{
id: blockModel
function appendBlock(block)
{
blockModel.append(block);
}
function appendTransaction(tr)
{
blockModel.get(blockModel.count - 1).transactions.append(tr)
}
function removeTransaction(blockIndex, trIndex)
{
blockModel.get(blockIndex).transactions.remove(trIndex)
}
function removeLastBlock()
{
blockModel.remove(blockModel.count - 1)
}
function removeBlock(index)
{
blockModel.remove(index)
}
function getTransaction(block, tr)
{
return blockModel.get(block).transactions.get(tr)
}
function setTransaction(blockIndex, trIndex, tr)
{
blockModel.get(blockIndex).transactions.set(trIndex, tr)
}
function setTransactionProperty(blockIndex, trIndex, propertyName, value)
{
blockModel.get(blockIndex).transactions.set(trIndex, { propertyName: value })
}
}
Rectangle
{
Layout.preferredWidth: parent.width
Layout.preferredHeight: 70
color: "transparent"
10 years ago
RowLayout
{
10 years ago
anchors.horizontalCenter: parent.horizontalCenter
10 years ago
anchors.top: parent.top
anchors.topMargin: 10
10 years ago
spacing: 20
Rectangle {
Layout.preferredWidth: 100
Layout.preferredHeight: 30
10 years ago
ScenarioButton {
id: rebuild
text: qsTr("Rebuild")
width: 100
height: 30
roundLeft: true
roundRight: true
property variant contractsHex: ({})
property variant txSha3: ({})
property variant accountsSha3
property variant contractsSha3
property variant txChanged: []
property var blinkReasons: []
function needRebuild(reason)
{
rebuild.startBlinking()
blinkReasons.push(reason)
}
function containsRebuildCause(reason)
{
for (var c in blinkReasons)
{
if (blinkReasons[c] === reason)
return true
}
10 years ago
return false
}
function notNeedRebuild(reason)
{
for (var c in blinkReasons)
{
if (blinkReasons[c] === reason)
{
blinkReasons.splice(c, 1)
break
}
}
if (blinkReasons.length === 0)
rebuild.stopBlinking()
}
10 years ago
onClicked:
10 years ago
{
10 years ago
if (ensureNotFuturetime.running)
return;
rebuilding()
10 years ago
stopBlinking()
states = []
10 years ago
var retBlocks = [];
var bAdded = 0;
for (var j = 0; j < model.blocks.length; j++)
10 years ago
{
10 years ago
var b = model.blocks[j];
var block = {
hash: b.hash,
number: b.number,
transactions: [],
status: b.status
}
for (var k = 0; k < model.blocks[j].transactions.length; k++)
{
if (blockModel.get(j).transactions.get(k).saveStatus)
{
var tr = model.blocks[j].transactions[k]
tr.saveStatus = true
block.transactions.push(tr);
}
}
if (block.transactions.length > 0)
10 years ago
{
10 years ago
bAdded++
block.number = bAdded
block.status = "mined"
retBlocks.push(block)
10 years ago
}
}
10 years ago
if (retBlocks.length === 0)
retBlocks.push(projectModel.stateListModel.createEmptyBlock())
else
10 years ago
{
10 years ago
var last = retBlocks[retBlocks.length - 1]
last.number = -1
last.status = "pending"
10 years ago
}
10 years ago
model.blocks = retBlocks
blockModel.clear()
for (var j = 0; j < model.blocks.length; j++)
blockModel.append(model.blocks[j])
10 years ago
10 years ago
ensureNotFuturetime.start()
takeCodeSnapshot()
takeTxSnaphot()
takeAccountsSnapshot()
takeContractsSnapShot()
blinkReasons = []
clientModel.setupScenario(model);
10 years ago
}
function takeContractsSnapShot()
{
contractsSha3 = codeModel.sha3(JSON.stringify(model.contracts))
}
function takeAccountsSnapshot()
{
accountsSha3 = codeModel.sha3(JSON.stringify(model.accounts))
}
function takeCodeSnapshot()
{
contractsHex = {}
for (var c in codeModel.contracts)
contractsHex[c] = codeModel.contracts[c].codeHex
}
function takeTxSnaphot()
{
txSha3 = {}
txChanged = []
for (var j = 0; j < model.blocks.length; j++)
{
for (var k = 0; k < model.blocks[j].transactions.length; k++)
{
if (txSha3[j] === undefined)
txSha3[j] = {}
txSha3[j][k] = codeModel.sha3(JSON.stringify(model.blocks[j].transactions[k]))
}
}
}
10 years ago
buttonShortcut: ""
sourceImg: "qrc:/qml/img/recycleicon@2x.png"
10 years ago
}
10 years ago
}
10 years ago
Rectangle
{
Layout.preferredWidth: 200
Layout.preferredHeight: 30
color: "transparent"
10 years ago
10 years ago
ScenarioButton {
id: addTransaction
text: qsTr("Add Tx")
onClicked:
10 years ago
{
if (model && model.blocks)
10 years ago
{
var lastBlock = model.blocks[model.blocks.length - 1];
if (lastBlock.status === "mined")
{
var newblock = projectModel.stateListModel.createEmptyBlock()
blockModel.appendBlock(newblock)
model.blocks.push(newblock);
}
10 years ago
var item = TransactionHelper.defaultTransaction()
transactionDialog.stateAccounts = model.accounts
transactionDialog.execute = true
transactionDialog.editMode = false
transactionDialog.open(model.blocks[model.blocks.length - 1].transactions.length, model.blocks.length - 1, item)
}
10 years ago
}
width: 100
height: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/sendtransactionicon@2x.png"
roundLeft: true
roundRight: false
10 years ago
}
10 years ago
Timer
{
id: ensureNotFuturetime
interval: 1000
repeat: false
running: false
}
10 years ago
10 years ago
Rectangle
10 years ago
{
10 years ago
width: 1
height: parent.height
anchors.right: addBlockBtn.left
color: "#ededed"
}
ScenarioButton {
id: addBlockBtn
text: qsTr("Add Block...")
10 years ago
anchors.left: addTransaction.right
roundLeft: false
roundRight: true
onClicked:
10 years ago
{
10 years ago
if (ensureNotFuturetime.running)
return
if (clientModel.mining || clientModel.running)
return
if (model.blocks.length > 0)
10 years ago
{
10 years ago
var lastBlock = model.blocks[model.blocks.length - 1]
if (lastBlock.status === "pending")
{
ensureNotFuturetime.start()
clientModel.mine()
}
else
addNewBlock()
10 years ago
}
else
addNewBlock()
}
10 years ago
function addNewBlock()
{
var block = projectModel.stateListModel.createEmptyBlock()
model.blocks.push(block)
blockModel.appendBlock(block)
}
width: 100
height: 30
10 years ago
10 years ago
buttonShortcut: ""
sourceImg: "qrc:/qml/img/newblock@2x.png"
10 years ago
}
}
10 years ago
10 years ago
Connections
{
target: clientModel
onNewBlock:
{
if (!clientModel.running)
{
var lastBlock = model.blocks[model.blocks.length - 1]
lastBlock.status = "mined"
lastBlock.number = model.blocks.length
var lastB = blockModel.get(model.blocks.length - 1)
lastB.status = "mined"
lastB.number = model.blocks.length
addBlockBtn.addNewBlock()
}
}
onStateCleared:
{
}
onNewRecord:
{
var blockIndex = parseInt(_r.transactionIndex.split(":")[0]) - 1
var trIndex = parseInt(_r.transactionIndex.split(":")[1])
if (blockIndex <= model.blocks.length - 1)
{
var item = model.blocks[blockIndex]
if (trIndex <= item.transactions.length - 1)
{
var tr = item.transactions[trIndex]
tr.returned = _r.returned
tr.recordIndex = _r.recordIndex
tr.logs = _r.logs
tr.sender = _r.sender
tr.returnParameters = _r.returnParameters
10 years ago
var trModel = blockModel.getTransaction(blockIndex, trIndex)
trModel.returned = _r.returned
trModel.recordIndex = _r.recordIndex
trModel.logs = _r.logs
trModel.sender = _r.sender
trModel.returnParameters = _r.returnParameters
10 years ago
blockModel.setTransaction(blockIndex, trIndex, trModel)
blockChainRepeater.select(blockIndex, trIndex)
10 years ago
return;
}
}
// tr is not in the list.
var itemTr = TransactionHelper.defaultTransaction()
itemTr.saveStatus = false
itemTr.functionId = _r.function
itemTr.contractId = _r.contract
itemTr.gasAuto = true
itemTr.parameters = _r.parameters
itemTr.isContractCreation = itemTr.functionId === itemTr.contractId
itemTr.label = _r.label
10 years ago
itemTr.isFunctionCall = itemTr.functionId !== "" && itemTr.functionId !== "<none>"
10 years ago
itemTr.returned = _r.returned
itemTr.value = QEtherHelper.createEther(_r.value, QEther.Wei)
itemTr.sender = _r.sender
itemTr.recordIndex = _r.recordIndex
itemTr.logs = _r.logs
itemTr.returnParameters = _r.returnParameters
10 years ago
model.blocks[model.blocks.length - 1].transactions.push(itemTr)
blockModel.appendTransaction(itemTr)
blockChainRepeater.select(blockIndex, trIndex)
10 years ago
}
onNewState: {
states[_record] = _accounts
}
10 years ago
onMiningComplete:
{
}
}
ScenarioButton {
id: newAccount
text: qsTr("New Account...")
10 years ago
onClicked: {
10 years ago
var ac = projectModel.stateListModel.newAccount("O", QEther.Wei)
model.accounts.push(ac)
clientModel.addAccount(ac.secret);
for (var k in Object.keys(blockChainPanel.states))
blockChainPanel.states[k].accounts["0x" + ac.address] = "0 wei" // add the account in all the previous state (balance at O)
10 years ago
accountAdded("0x" + ac.address, "0")
10 years ago
}
Layout.preferredWidth: 100
Layout.preferredHeight: 30
buttonShortcut: ""
sourceImg: "qrc:/qml/img/newaccounticon@2x.png"
10 years ago
roundLeft: true
roundRight: true
10 years ago
}
}
}
TransactionDialog {
id: transactionDialog
property bool execute
onAccepted: {
var item = transactionDialog.getItem()
if (execute)
{
var lastBlock = model.blocks[model.blocks.length - 1];
if (lastBlock.status === "mined")
{
var newBlock = projectModel.stateListModel.createEmptyBlock();
model.blocks.push(newBlock);
blockModel.appendBlock(newBlock)
}
if (!clientModel.running)
clientModel.executeTr(item)
}
else {
model.blocks[blockIndex].transactions[transactionIndex] = item
blockModel.setTransaction(blockIndex, transactionIndex, item)
chainChanged(blockIndex, transactionIndex, item)
10 years ago
}
}
}
}