import QtQuick 2.2
import QtQuick . Controls 1.1
import QtQuick . Controls . Styles 1.1
import QtQuick . Dialogs 1.1
import QtQuick . Window 2.2
import QtQuick . Layouts 1.1
import org . ethereum . qml . QEther 1.0
import "js/QEtherHelper.js" as QEtherHelper
import "js/TransactionHelper.js" as TransactionHelper
Item {
property alias model: stateListModel
property var stateList: [ ]
property alias stateDialog: stateDialog
property string defaultAccount: "cb73d9408c4720e230387d956eb0f829d8a4dd2c1055f96257167e14e7169074" //support for old project
function fromPlainStateItem ( s ) {
if ( ! s . accounts )
s . accounts = [ stateListModel . newAccount ( "1000000" , QEther . Ether , defaultAccount ) ] ; //support for old project
if ( ! s . contracts )
s . contracts = [ ] ;
var ret = { } ;
ret . title = s . title ;
ret . transactions = s . transactions . filter ( function ( t ) { return ! t . stdContract ; } ) . map ( fromPlainTransactionItem ) ; //support old projects by filtering std contracts
if ( s . blocks )
ret . blocks = s . blocks . map ( fromPlainBlockItem ) ;
ret . accounts = s . accounts . map ( fromPlainAccountItem ) ;
ret . contracts = s . contracts . map ( fromPlainAccountItem ) ;
ret . miner = s . miner ;
// support old projects
if ( ! ret . blocks )
{
ret . blocks = [ {
hash: "" ,
number: - 1 ,
transactions: [ ] ,
status: "pending"
} ]
for ( var j in ret . transactions )
ret . blocks [ 0 ] . transactions . push ( fromPlainTransactionItem ( toPlainTransactionItem ( ret . transactions [ j ] ) ) )
}
return ret ;
}
function fromPlainAccountItem ( t )
{
return {
name: t . name ,
address: t . address ,
secret: t . secret ,
balance: QEtherHelper . createEther ( t . balance . value , t . balance . unit ) ,
storage: t . storage ,
code: t . code ,
} ;
}
function fromPlainTransactionItem ( t ) {
if ( ! t . sender )
t . sender = defaultAccount ; //support for old project
var r = {
type: t . type ,
contractId: t . contractId ,
functionId: t . functionId ,
url: t . url ,
value: QEtherHelper . createEther ( t . value . value , t . value . unit ) ,
gas: QEtherHelper . createBigInt ( t . gas . value ) ,
gasPrice: QEtherHelper . createEther ( t . gasPrice . value , t . gasPrice . unit ) ,
gasAuto: t . gasAuto ,
parameters: { } ,
sender: t . sender ,
isContractCreation: t . isContractCreation ,
label: t . label ,
isFunctionCall: t . isFunctionCall ,
saveStatus: t . saveStatus
} ;
if ( r . saveStatus === undefined )
r . saveStatus = true
if ( r . isFunctionCall === undefined )
r . isFunctionCall = true ;
if ( ! r . label )
r . label = r . contractId + " - " + r . functionId ;
if ( r . isContractCreation === undefined )
r . isContractCreation = r . functionId === r . contractId ;
for ( var key in t . parameters )
r . parameters [ key ] = t . parameters [ key ] ;
return r ;
}
function fromPlainBlockItem ( b )
{
var r = {
hash: b . hash ,
number: b . number ,
transactions: b . transactions . filter ( function ( t ) { return ! t . stdContract ; } ) . map ( fromPlainTransactionItem ) , //support old projects by filtering std contracts
status: b . status
}
return r ;
}
function toPlainStateItem ( s ) {
return {
title: s . title ,
blocks: s . blocks . map ( toPlainBlockItem ) ,
transactions: s . transactions . map ( toPlainTransactionItem ) ,
accounts: s . accounts . map ( toPlainAccountItem ) ,
contracts: s . contracts . map ( toPlainAccountItem ) ,
miner: s . miner
} ;
}
function getParamType ( param , params )
{
for ( var k in params )
{
if ( params [ k ] . declaration . name === param )
return params [ k ] . declaration . type ;
}
return '' ;
}
function toPlainBlockItem ( b )
{
var r = {
hash: b . hash ,
number: b . number ,
transactions: b . transactions . map ( toPlainTransactionItem ) ,
status: b . status
}
return r ;
}
function toPlainAccountItem ( t )
{
return {
name: t . name ,
secret: t . secret ,
balance: {
value: t . balance . value ,
unit: t . balance . unit
} ,
address: t . address ,
storage: t . storage ,
code: t . code ,
} ;
}
function toPlainTransactionItem ( t ) {
var r = {
type: t . type ,
contractId: t . contractId ,
functionId: t . functionId ,
url: t . url ,
value: { value: t . value . value , unit: t . value . unit } ,
gas: { value: t . gas . value ( ) } ,
gasAuto: t . gasAuto ,
gasPrice: { value: t . gasPrice . value , unit: t . gasPrice . unit } ,
sender: t . sender ,
parameters: { } ,
isContractCreation: t . isContractCreation ,
label: t . label ,
isFunctionCall: t . isFunctionCall ,
saveStatus: t . saveStatus
} ;
for ( var key in t . parameters )
r . parameters [ key ] = t . parameters [ key ] ;
return r ;
}
Connections {
target: projectModel
onProjectClosed: {
stateListModel . clear ( ) ;
stateList = [ ] ;
codeModel . reset ( ) ;
}
onProjectLoading: stateListModel . loadStatesFromProject ( projectData ) ;
onProjectFileSaving: {
projectData . states = [ ]
for ( var i = 0 ; i < stateListModel . count ; i ++ ) {
projectData . states . push ( toPlainStateItem ( stateList [ i ] ) ) ;
}
projectData . defaultStateIndex = stateListModel . defaultStateIndex ;
stateListModel . data = projectData
}
onNewProject: {
var state = toPlainStateItem ( stateListModel . createDefaultState ( ) ) ;
state . title = qsTr ( "Default" ) ;
projectData . states = [ state ] ;
projectData . defaultStateIndex = 0 ;
stateListModel . loadStatesFromProject ( projectData ) ;
}
}
Connections {
target: codeModel
onNewContractCompiled: {
stateListModel . addNewContracts ( ) ;
}
onContractRenamed: {
stateListModel . renameContracts ( _oldName , _newName ) ;
}
}
StateDialog {
id: stateDialog
onAccepted: {
var item = stateDialog . getItem ( ) ;
saveState ( item ) ;
}
function saveState ( item )
{
if ( stateDialog . stateIndex < stateListModel . count ) {
if ( stateDialog . isDefault )
stateListModel . defaultStateIndex = stateIndex ;
stateList [ stateDialog . stateIndex ] = item ;
stateListModel . set ( stateDialog . stateIndex , item ) ;
} else {
if ( stateDialog . isDefault )
stateListModel . defaultStateIndex = 0 ;
stateList . push ( item ) ;
stateListModel . append ( item ) ;
}
if ( stateDialog . isDefault )
stateListModel . defaultStateChanged ( ) ;
stateListModel . save ( ) ;
}
}
ListModel {
id: stateListModel
property int defaultStateIndex: 0
property variant data
signal defaultStateChanged ;
signal stateListModelReady ;
signal stateRun ( int index )
signal stateDeleted ( int index )
function defaultTransactionItem ( ) {
return TransactionHelper . defaultTransaction ( ) ;
}
function newAccount ( _balance , _unit , _secret )
{
if ( ! _secret )
_secret = clientModel . newSecret ( ) ;
var address = clientModel . address ( _secret ) ;
var name = qsTr ( "Account" ) + "-" + address . substring ( 0 , 4 ) ;
var amount = QEtherHelper . createEther ( _balance , _unit )
return { name: name , secret: _secret , balance: amount , address: address } ;
}
function duplicateState ( index )
{
var state = stateList [ index ]
var item = fromPlainStateItem ( toPlainStateItem ( state ) )
item . title = qsTr ( "Copy of" ) + " " + state . title
appendState ( item )
save ( )
}
function createEmptyBlock ( )
{
return {
hash: "" ,
number: - 1 ,
transactions: [ ] ,
status: "pending"
}
}
function createDefaultState ( ) {
var item = {
title: "" ,
transactions: [ ] ,
accounts: [ ] ,
contracts: [ ] ,
blocks: [ { status: "pending" , number: - 1 , hash: "" , transactions: [ ] } ]
} ;
var account = newAccount ( "1000000" , QEther . Ether , defaultAccount )
item . accounts . push ( account ) ;
item . miner = account ;
//add constructors, //TODO: order by dependencies
for ( var c in codeModel . contracts ) {
var ctorTr = defaultTransactionItem ( ) ;
ctorTr . functionId = c ;
ctorTr . contractId = c ;
ctorTr . label = qsTr ( "Deploy" ) + " " + ctorTr . contractId ;
ctorTr . sender = item . accounts [ 0 ] . secret ;
item . transactions . push ( ctorTr ) ;
item . blocks [ 0 ] . transactions . push ( ctorTr )
}
return item ;
}
function renameContracts ( oldName , newName ) {
var changed = false ;
for ( var c in codeModel . contracts ) {
for ( var s = 0 ; s < stateListModel . count ; s ++ ) {
var state = stateList [ s ] ;
for ( var t = 0 ; t < state . transactions . length ; t ++ ) {
var transaction = state . transactions [ t ] ;
if ( transaction . contractId === oldName ) {
transaction . contractId = newName ;
if ( transaction . functionId === oldName )
transaction . functionId = newName ;
changed = true ;
state . transactions [ t ] = transaction ;
}
}
stateListModel . set ( s , state ) ;
stateList [ s ] = state ;
}
}
if ( changed )
save ( ) ;
}
function addNewContracts ( ) {
//add new contracts to empty states
var changed = false ;
for ( var c in codeModel . contracts ) {
for ( var s = 0 ; s < stateListModel . count ; s ++ ) {
var state = stateList [ s ] ;
if ( state . transactions . length === 0 ) {
//append this contract
var ctorTr = defaultTransactionItem ( ) ;
ctorTr . functionId = c ;
ctorTr . contractId = c ;
ctorTr . label = qsTr ( "Deploy" ) + " " + ctorTr . contractId ;
ctorTr . sender = state . accounts [ 0 ] . secret ;
state . transactions . push ( ctorTr ) ;
changed = true ;
stateListModel . set ( s , state ) ;
stateList [ s ] = state ;
}
}
}
if ( changed )
save ( ) ;
}
function addState ( ) {
var item = createDefaultState ( ) ;
stateDialog . open ( stateListModel . count , item , false ) ;
}
function appendState ( item )
{
stateListModel . append ( item ) ;
stateList . push ( item ) ;
}
function editState ( index ) {
stateDialog . open ( index , stateList [ index ] , defaultStateIndex === index ) ;
}
function getState ( index ) {
return stateList [ index ] ;
}
function debugDefaultState ( ) {
if ( defaultStateIndex >= 0 && defaultStateIndex < stateList . length )
runState ( defaultStateIndex ) ;
}
function runState ( index ) {
var item = stateList [ index ] ;
clientModel . setupScenario ( item ) ;
stateRun ( index ) ;
}
function deleteState ( index ) {
stateListModel . remove ( index ) ;
stateList . splice ( index , 1 ) ;
if ( index === defaultStateIndex )
{
defaultStateIndex = 0 ;
defaultStateChanged ( ) ;
}
else if ( defaultStateIndex > index )
defaultStateIndex -- ;
save ( ) ;
stateDeleted ( index ) ;
}
function save ( ) {
projectModel . saveProject ( ) ;
}
function defaultStateName ( )
{
if ( stateList . length > 0 )
return stateList [ defaultStateIndex ] . title ;
else
return ""
}
function reloadStateFromFromProject ( index )
{
if ( data )
{
var item = fromPlainStateItem ( data . states [ index ] )
stateListModel . set ( index , item )
stateList [ index ] = item
return item
}
}
function loadStatesFromProject ( projectData )
{
data = projectData
if ( ! projectData . states )
projectData . states = [ ] ;
if ( projectData . defaultStateIndex !== undefined )
defaultStateIndex = projectData . defaultStateIndex ;
else
defaultStateIndex = 0 ;
var items = projectData . states ;
stateListModel . clear ( ) ;
stateList = [ ] ;
for ( var i = 0 ; i < items . length ; i ++ ) {
var item = fromPlainStateItem ( items [ i ] ) ;
stateListModel . append ( item ) ;
stateList . push ( item ) ;
}
stateListModelReady ( ) ;
}
}
}