From c5704eb44618826c27e13d7a8a73531e698e0d79 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 8 May 2019 11:33:31 +1000 Subject: [PATCH 1/6] simple account permissioning --- .../contracts/SimpleAccountPermissioning.sol | 37 +++++++++++++++ .../migrations/2_deploy_contracts.js | 2 + .../test/test-account-permissioning.js | 47 +++++++++++++++++++ ...ssioning.js => test-node-permissioning.js} | 6 +-- 4 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 acceptance-tests/simple-permissioning-smart-contract/contracts/SimpleAccountPermissioning.sol create mode 100644 acceptance-tests/simple-permissioning-smart-contract/test/test-account-permissioning.js rename acceptance-tests/simple-permissioning-smart-contract/test/{test-permissioning.js => test-node-permissioning.js} (90%) diff --git a/acceptance-tests/simple-permissioning-smart-contract/contracts/SimpleAccountPermissioning.sol b/acceptance-tests/simple-permissioning-smart-contract/contracts/SimpleAccountPermissioning.sol new file mode 100644 index 0000000000..ae302cca40 --- /dev/null +++ b/acceptance-tests/simple-permissioning-smart-contract/contracts/SimpleAccountPermissioning.sol @@ -0,0 +1,37 @@ +pragma solidity >=0.4.0 <0.6.0; +// THIS CONTRACT IS FOR TESTING PURPOSES ONLY +// DO NOT USE THIS CONTRACT IN PRODUCTION APPLICATIONS + +contract SimpleAccountPermissioning { + address[] private whitelist; + + function transactionAllowed( + address sender, + address target, + uint256 value, + uint256 gasPrice, + uint256 gasLimit, + bytes memory payload) + public view returns (bool) { + return whitelistContains(sender); + } + function addAccount(address account) public { + whitelist.push(account); + } + function removeAccount(address account) public { + for (uint256 i = 0; i < whitelist.length; i++) { + if (whitelist[i] == account) { + whitelist[i] = whitelist[whitelist.length - 1]; + whitelist.length --; + } + } + } + function whitelistContains(address account) public view returns(bool) { + for (uint256 i = 0; i < whitelist.length; i++) { + if (whitelist[i] == account) { + return true; + } + } + return false; + } +} diff --git a/acceptance-tests/simple-permissioning-smart-contract/migrations/2_deploy_contracts.js b/acceptance-tests/simple-permissioning-smart-contract/migrations/2_deploy_contracts.js index 25e5754bcc..d3c1ebfcd1 100644 --- a/acceptance-tests/simple-permissioning-smart-contract/migrations/2_deploy_contracts.js +++ b/acceptance-tests/simple-permissioning-smart-contract/migrations/2_deploy_contracts.js @@ -1,5 +1,7 @@ var SimplePermissioning = artifacts.require("SimplePermissioning"); +var SimpleAccountPermissioning = artifacts.require("SimpleAccountPermissioning"); module.exports = function(deployer) { deployer.deploy(SimplePermissioning); + deployer.deploy(SimpleAccountPermissioning); }; diff --git a/acceptance-tests/simple-permissioning-smart-contract/test/test-account-permissioning.js b/acceptance-tests/simple-permissioning-smart-contract/test/test-account-permissioning.js new file mode 100644 index 0000000000..bd9a19c5ff --- /dev/null +++ b/acceptance-tests/simple-permissioning-smart-contract/test/test-account-permissioning.js @@ -0,0 +1,47 @@ +const TestPermissioning = artifacts.require('SimpleAccountPermissioning.sol'); +var proxy; + +var address1 = "0x627306090abaB3A6e1400e9345bC60c78a8BEf57"; +var address2 = "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73"; + +var value = 99; +var gasPrice = 88; +var gasLimit = 77; +var payload = "0x1234"; + +contract('Permissioning: Accounts', () => { + describe('Function: permissioning', () => { + + it('Should NOT permit any transaction when acount whitelist is empty', async () => { + proxy = await TestPermissioning.new(); + let permitted = await proxy.transactionAllowed(address1, address2, value, gasPrice, gasLimit, payload); + assert.equal(permitted, false, 'expected tx NOT permitted'); + }); + + it('Should add an account to the whitelist and then permit that node', async () => { + await proxy.addAccount(address1); + let permitted = await proxy.transactionAllowed(address1, address2, value, gasPrice, gasLimit, payload); + assert.equal(permitted, true, 'added address1: expected tx1 to be permitted'); + + // await another + await proxy.addAccount(address2); + permitted = await proxy.transactionAllowed(address2, address1, value, gasPrice, gasLimit, payload); + assert.equal(permitted, true, 'added address2: expected tx2 to be permitted'); + + // first one still permitted + permitted = await proxy.transactionAllowed(address1, address2, value, gasPrice, gasLimit, payload); + assert.equal(permitted, true, 'expected tx from address1 to still be permitted'); + }); + + it('Should remove an account from the whitelist and then NOT permit that account to send tx', async () => { + await proxy.removeAccount(address2); + let permitted = await proxy.transactionAllowed(address2, address1, value, gasPrice, gasLimit, payload); + assert.equal(permitted, false, 'expected removed account (address2) NOT permitted to send tx'); + + // first one still permitted + permitted = await proxy.transactionAllowed(address1, address2, value, gasPrice, gasLimit, payload); + assert.equal(permitted, true, 'expected tx from address1 to still be permitted'); + }); + + }); +}); diff --git a/acceptance-tests/simple-permissioning-smart-contract/test/test-permissioning.js b/acceptance-tests/simple-permissioning-smart-contract/test/test-node-permissioning.js similarity index 90% rename from acceptance-tests/simple-permissioning-smart-contract/test/test-permissioning.js rename to acceptance-tests/simple-permissioning-smart-contract/test/test-node-permissioning.js index d7a0769d6e..109c596bb0 100644 --- a/acceptance-tests/simple-permissioning-smart-contract/test/test-permissioning.js +++ b/acceptance-tests/simple-permissioning-smart-contract/test/test-node-permissioning.js @@ -11,7 +11,7 @@ var node2Low = "0x892092b7fcb320c1b62f3759bd359fdc3a2ed5df436c3d8914b15327401289 var node2Host = "0x596c3d8914b1532fdc3a2ed5df439bd3"; var node2Port = 30304; -contract('Permissioning', () => { +contract('Permissioning: Nodes', () => { describe('Function: permissioning', () => { it('Should NOT permit any node when none have been added', async () => { @@ -46,7 +46,7 @@ contract('Permissioning', () => { it('Should allow a connection between 2 added nodes', async () => { let permitted = await proxy.connectionAllowed(node1High, node1Low, node1Host, node1Port, node2High, node2Low, node2Host, node2Port); - assert.equal(permitted, true, 'expected 2 added nodes to work as source <> destination'); + assert.equal(permitted, '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 'expected 2 added nodes to work as source <> destination'); }); it('Should remove a node from the whitelist and then NOT permit that node', async () => { @@ -55,7 +55,7 @@ contract('Permissioning', () => { assert.equal(permitted, false, 'expected removed node NOT permitted'); permitted = await proxy.connectionAllowed(node1High, node1Low, node1Host, node1Port, node2High, node2Low, node2Host, node2Port); - assert.equal(permitted, false, 'expected source disallowed since it was removed'); + assert.equal(permitted, '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 'expected source disallowed since it was removed'); }); From aa98e202e7f719b6f97acd95aa2325f9a04108a5 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 8 May 2019 13:44:07 +1000 Subject: [PATCH 2/6] remove describe --- .../test/test-account-permissioning.js | 60 ++++++------- .../test/test-node-permissioning.js | 88 +++++++++---------- 2 files changed, 70 insertions(+), 78 deletions(-) diff --git a/acceptance-tests/simple-permissioning-smart-contract/test/test-account-permissioning.js b/acceptance-tests/simple-permissioning-smart-contract/test/test-account-permissioning.js index bd9a19c5ff..d4615acd77 100644 --- a/acceptance-tests/simple-permissioning-smart-contract/test/test-account-permissioning.js +++ b/acceptance-tests/simple-permissioning-smart-contract/test/test-account-permissioning.js @@ -10,38 +10,34 @@ var gasLimit = 77; var payload = "0x1234"; contract('Permissioning: Accounts', () => { - describe('Function: permissioning', () => { - - it('Should NOT permit any transaction when acount whitelist is empty', async () => { - proxy = await TestPermissioning.new(); - let permitted = await proxy.transactionAllowed(address1, address2, value, gasPrice, gasLimit, payload); - assert.equal(permitted, false, 'expected tx NOT permitted'); - }); - - it('Should add an account to the whitelist and then permit that node', async () => { - await proxy.addAccount(address1); - let permitted = await proxy.transactionAllowed(address1, address2, value, gasPrice, gasLimit, payload); - assert.equal(permitted, true, 'added address1: expected tx1 to be permitted'); - - // await another - await proxy.addAccount(address2); - permitted = await proxy.transactionAllowed(address2, address1, value, gasPrice, gasLimit, payload); - assert.equal(permitted, true, 'added address2: expected tx2 to be permitted'); - - // first one still permitted - permitted = await proxy.transactionAllowed(address1, address2, value, gasPrice, gasLimit, payload); - assert.equal(permitted, true, 'expected tx from address1 to still be permitted'); - }); - - it('Should remove an account from the whitelist and then NOT permit that account to send tx', async () => { - await proxy.removeAccount(address2); - let permitted = await proxy.transactionAllowed(address2, address1, value, gasPrice, gasLimit, payload); - assert.equal(permitted, false, 'expected removed account (address2) NOT permitted to send tx'); - - // first one still permitted - permitted = await proxy.transactionAllowed(address1, address2, value, gasPrice, gasLimit, payload); - assert.equal(permitted, true, 'expected tx from address1 to still be permitted'); - }); + it('Should NOT permit any transaction when acount whitelist is empty', async () => { + proxy = await TestPermissioning.new(); + let permitted = await proxy.transactionAllowed(address1, address2, value, gasPrice, gasLimit, payload); + assert.equal(permitted, false, 'expected tx NOT permitted'); + }); + + it('Should add an account to the whitelist and then permit that node', async () => { + await proxy.addAccount(address1); + let permitted = await proxy.transactionAllowed(address1, address2, value, gasPrice, gasLimit, payload); + assert.equal(permitted, true, 'added address1: expected tx1 to be permitted'); + + // await another + await proxy.addAccount(address2); + permitted = await proxy.transactionAllowed(address2, address1, value, gasPrice, gasLimit, payload); + assert.equal(permitted, true, 'added address2: expected tx2 to be permitted'); + + // first one still permitted + permitted = await proxy.transactionAllowed(address1, address2, value, gasPrice, gasLimit, payload); + assert.equal(permitted, true, 'expected tx from address1 to still be permitted'); + }); + + it('Should remove an account from the whitelist and then NOT permit that account to send tx', async () => { + await proxy.removeAccount(address2); + let permitted = await proxy.transactionAllowed(address2, address1, value, gasPrice, gasLimit, payload); + assert.equal(permitted, false, 'expected removed account (address2) NOT permitted to send tx'); + // first one still permitted + permitted = await proxy.transactionAllowed(address1, address2, value, gasPrice, gasLimit, payload); + assert.equal(permitted, true, 'expected tx from address1 to still be permitted'); }); }); diff --git a/acceptance-tests/simple-permissioning-smart-contract/test/test-node-permissioning.js b/acceptance-tests/simple-permissioning-smart-contract/test/test-node-permissioning.js index 109c596bb0..92fa5c39db 100644 --- a/acceptance-tests/simple-permissioning-smart-contract/test/test-node-permissioning.js +++ b/acceptance-tests/simple-permissioning-smart-contract/test/test-node-permissioning.js @@ -12,52 +12,48 @@ var node2Host = "0x596c3d8914b1532fdc3a2ed5df439bd3"; var node2Port = 30304; contract('Permissioning: Nodes', () => { - describe('Function: permissioning', () => { - - it('Should NOT permit any node when none have been added', async () => { - proxy = await TestPermissioning.new(); - let permitted = await proxy.enodeAllowed(node1High, node1Low, node1Host, node1Port); - assert.equal(permitted, false, 'expected node NOT permitted'); - }); - - it('Should compute key', async () => { - let key1 = await proxy.computeKey(node1High, node1Low, node1Host, node1Port); - let key2 = await proxy.computeKey(node1High, node1Low, node1Host, node1Port); - assert.equal(key1, key2, "computed keys should be the same"); - - let key3 = await proxy.computeKey(node1High, node1Low, node1Host, node2Port); - assert(key3 != key2, "keys for different ports should be different"); - }); - - it('Should add a node to the whitelist and then permit that node', async () => { - await proxy.addEnode(node1High, node1Low, node1Host, node1Port); - let permitted = await proxy.enodeAllowed(node1High, node1Low, node1Host, node1Port); - assert.equal(permitted, true, 'expected node added to be permitted'); - - // await another - await proxy.addEnode(node2High, node2Low, node2Host, node2Port); - permitted = await proxy.enodeAllowed(node2High, node2Low, node2Host, node2Port); - assert.equal(permitted, true, 'expected node 2 added to be permitted'); - - // first one still permitted - permitted = await proxy.enodeAllowed(node1High, node1Low, node1Host, node1Port); - assert.equal(permitted, true, 'expected node 1 added to be permitted'); - }); - - it('Should allow a connection between 2 added nodes', async () => { - let permitted = await proxy.connectionAllowed(node1High, node1Low, node1Host, node1Port, node2High, node2Low, node2Host, node2Port); - assert.equal(permitted, '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 'expected 2 added nodes to work as source <> destination'); - }); - - it('Should remove a node from the whitelist and then NOT permit that node', async () => { - await proxy.removeEnode(node1High, node1Low, node1Host, node1Port); - let permitted = await proxy.enodeAllowed(node1High, node1Low, node1Host, node1Port); - assert.equal(permitted, false, 'expected removed node NOT permitted'); - - permitted = await proxy.connectionAllowed(node1High, node1Low, node1Host, node1Port, node2High, node2Low, node2Host, node2Port); - assert.equal(permitted, '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 'expected source disallowed since it was removed'); - - }); + it('Should NOT permit any node when none have been added', async () => { + proxy = await TestPermissioning.new(); + let permitted = await proxy.enodeAllowed(node1High, node1Low, node1Host, node1Port); + assert.equal(permitted, false, 'expected node NOT permitted'); + }); + + it('Should compute key', async () => { + let key1 = await proxy.computeKey(node1High, node1Low, node1Host, node1Port); + let key2 = await proxy.computeKey(node1High, node1Low, node1Host, node1Port); + assert.equal(key1, key2, "computed keys should be the same"); + + let key3 = await proxy.computeKey(node1High, node1Low, node1Host, node2Port); + assert(key3 != key2, "keys for different ports should be different"); + }); + + it('Should add a node to the whitelist and then permit that node', async () => { + await proxy.addEnode(node1High, node1Low, node1Host, node1Port); + let permitted = await proxy.enodeAllowed(node1High, node1Low, node1Host, node1Port); + assert.equal(permitted, true, 'expected node added to be permitted'); + + // await another + await proxy.addEnode(node2High, node2Low, node2Host, node2Port); + permitted = await proxy.enodeAllowed(node2High, node2Low, node2Host, node2Port); + assert.equal(permitted, true, 'expected node 2 added to be permitted'); + + // first one still permitted + permitted = await proxy.enodeAllowed(node1High, node1Low, node1Host, node1Port); + assert.equal(permitted, true, 'expected node 1 added to be permitted'); + }); + + it('Should allow a connection between 2 added nodes', async () => { + let permitted = await proxy.connectionAllowed(node1High, node1Low, node1Host, node1Port, node2High, node2Low, node2Host, node2Port); + assert.equal(permitted, '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 'expected 2 added nodes to work as source <> destination'); + }); + + it('Should remove a node from the whitelist and then NOT permit that node', async () => { + await proxy.removeEnode(node1High, node1Low, node1Host, node1Port); + let permitted = await proxy.enodeAllowed(node1High, node1Low, node1Host, node1Port); + assert.equal(permitted, false, 'expected removed node NOT permitted'); + permitted = await proxy.connectionAllowed(node1High, node1Low, node1Host, node1Port, node2High, node2Low, node2Host, node2Port); + assert.equal(permitted, '0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 'expected source disallowed since it was removed'); + }); }); From 9e9abf4345fbb779e939934b6b861ab3fce87882 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 8 May 2019 13:47:02 +1000 Subject: [PATCH 3/6] rename --- .../{SimplePermissioning.sol => SimpleNodePermissioning.sol} | 2 +- .../migrations/2_deploy_contracts.js | 4 ++-- .../test/test-node-permissioning.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename acceptance-tests/simple-permissioning-smart-contract/contracts/{SimplePermissioning.sol => SimpleNodePermissioning.sol} (98%) diff --git a/acceptance-tests/simple-permissioning-smart-contract/contracts/SimplePermissioning.sol b/acceptance-tests/simple-permissioning-smart-contract/contracts/SimpleNodePermissioning.sol similarity index 98% rename from acceptance-tests/simple-permissioning-smart-contract/contracts/SimplePermissioning.sol rename to acceptance-tests/simple-permissioning-smart-contract/contracts/SimpleNodePermissioning.sol index 36f0e48a70..0e5a2108c0 100644 --- a/acceptance-tests/simple-permissioning-smart-contract/contracts/SimplePermissioning.sol +++ b/acceptance-tests/simple-permissioning-smart-contract/contracts/SimpleNodePermissioning.sol @@ -2,7 +2,7 @@ pragma solidity >=0.4.0 <0.6.0; // THIS CONTRACT IS FOR TESTING PURPOSES ONLY // DO NOT USE THIS CONTRACT IN PRODUCTION APPLICATIONS -contract SimplePermissioning { +contract SimpleNodePermissioning { struct Enode { bytes32 enodeHigh; bytes32 enodeLow; diff --git a/acceptance-tests/simple-permissioning-smart-contract/migrations/2_deploy_contracts.js b/acceptance-tests/simple-permissioning-smart-contract/migrations/2_deploy_contracts.js index d3c1ebfcd1..b23928c0a2 100644 --- a/acceptance-tests/simple-permissioning-smart-contract/migrations/2_deploy_contracts.js +++ b/acceptance-tests/simple-permissioning-smart-contract/migrations/2_deploy_contracts.js @@ -1,7 +1,7 @@ -var SimplePermissioning = artifacts.require("SimplePermissioning"); +var SimpleNodePermissioning = artifacts.require("SimpleNodePermissioning"); var SimpleAccountPermissioning = artifacts.require("SimpleAccountPermissioning"); module.exports = function(deployer) { - deployer.deploy(SimplePermissioning); + deployer.deploy(SimpleNodePermissioning); deployer.deploy(SimpleAccountPermissioning); }; diff --git a/acceptance-tests/simple-permissioning-smart-contract/test/test-node-permissioning.js b/acceptance-tests/simple-permissioning-smart-contract/test/test-node-permissioning.js index 92fa5c39db..2af60d3452 100644 --- a/acceptance-tests/simple-permissioning-smart-contract/test/test-node-permissioning.js +++ b/acceptance-tests/simple-permissioning-smart-contract/test/test-node-permissioning.js @@ -1,4 +1,4 @@ -const TestPermissioning = artifacts.require('SimplePermissioning.sol'); +const TestPermissioning = artifacts.require('SimpleNodePermissioning.sol'); var proxy; var node1High = "0x9bd359fdc3a2ed5df436c3d8914b1532740128929892092b7fcb320c1b62f375"; From 29f07989594e32d06be9c3567719e38b06619844 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 8 May 2019 18:38:58 +1000 Subject: [PATCH 4/6] typo --- .../SmartContractNodePermissioningAcceptanceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/permissioning/SmartContractNodePermissioningAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/permissioning/SmartContractNodePermissioningAcceptanceTest.java index 7f974e5e9d..5ca7be39c8 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/permissioning/SmartContractNodePermissioningAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/permissioning/SmartContractNodePermissioningAcceptanceTest.java @@ -30,7 +30,7 @@ public void setUp() { bootnode = bootnode("bootnode"); forbiddenNode = node("forbidden-node"); allowedNode = node("allowed-node"); - permissionedNode = permissionedNode("pemissioned-node"); + permissionedNode = permissionedNode("permissioned-node"); permissionedCluster.start(bootnode, forbiddenNode, allowedNode, permissionedNode); From 389391cdedfae9d2908d7d20a2f153bde97f7420 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 8 May 2019 20:09:31 +1000 Subject: [PATCH 5/6] changed array to mapping --- .../contracts/SimpleAccountPermissioning.sol | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/acceptance-tests/simple-permissioning-smart-contract/contracts/SimpleAccountPermissioning.sol b/acceptance-tests/simple-permissioning-smart-contract/contracts/SimpleAccountPermissioning.sol index ae302cca40..b1f6d1f307 100644 --- a/acceptance-tests/simple-permissioning-smart-contract/contracts/SimpleAccountPermissioning.sol +++ b/acceptance-tests/simple-permissioning-smart-contract/contracts/SimpleAccountPermissioning.sol @@ -3,7 +3,7 @@ pragma solidity >=0.4.0 <0.6.0; // DO NOT USE THIS CONTRACT IN PRODUCTION APPLICATIONS contract SimpleAccountPermissioning { - address[] private whitelist; + mapping (address => bool) private whitelist; function transactionAllowed( address sender, @@ -16,22 +16,12 @@ contract SimpleAccountPermissioning { return whitelistContains(sender); } function addAccount(address account) public { - whitelist.push(account); + whitelist[account] = true; } function removeAccount(address account) public { - for (uint256 i = 0; i < whitelist.length; i++) { - if (whitelist[i] == account) { - whitelist[i] = whitelist[whitelist.length - 1]; - whitelist.length --; - } - } + whitelist[account] = false; } function whitelistContains(address account) public view returns(bool) { - for (uint256 i = 0; i < whitelist.length; i++) { - if (whitelist[i] == account) { - return true; - } - } - return false; + return whitelist[account]; } } From 5b43f639ad6e2771314fc59b2d528f9d7e0553c4 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 9 May 2019 14:39:26 +1000 Subject: [PATCH 6/6] typo --- .../test/test-account-permissioning.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance-tests/simple-permissioning-smart-contract/test/test-account-permissioning.js b/acceptance-tests/simple-permissioning-smart-contract/test/test-account-permissioning.js index d4615acd77..6e467554e3 100644 --- a/acceptance-tests/simple-permissioning-smart-contract/test/test-account-permissioning.js +++ b/acceptance-tests/simple-permissioning-smart-contract/test/test-account-permissioning.js @@ -10,7 +10,7 @@ var gasLimit = 77; var payload = "0x1234"; contract('Permissioning: Accounts', () => { - it('Should NOT permit any transaction when acount whitelist is empty', async () => { + it('Should NOT permit any transaction when account whitelist is empty', async () => { proxy = await TestPermissioning.new(); let permitted = await proxy.transactionAllowed(address1, address2, value, gasPrice, gasLimit, payload); assert.equal(permitted, false, 'expected tx NOT permitted');