1

I am trying to implement a smart contract for identity management use case.

I am able to pass all the test cases which I have written in mocha with truffle.

I am compiling this smart contract using truffle and calling it from web3.py.

But when I add an entry in the mapping ( sicontracts ) with key as bytes32 ( Ipfs hash converted to bytes32 ) and value as struct SIContract in the function submitContract . It seems to be not getting saved in the mapping. When I verify this in function approveContract it always returns false. When I checked I could find that my mapping is not getting saved. Please help.

Solidity Code :

pragma solidity ^0.4.0;

 /**
 * The purpose of this contract is to provide a mechanism to verify idntity
 */

contract SmartIdentityRegistry {

address private owner;
uint constant PENDING = 0;
uint constant ACTIVE = 1;
uint constant REJECTED = 2;

/**
 * Constructor of the registry.
 */
function SmartIdentityRegistry() {
    owner = msg.sender;
}

/**
 * The SIContract structure: every SIContract is composed of:
 * - Hash of contract bytecode
 * - Account that submitted the address
 * - Status - 0 = pending, 1 = active, 2 = rejected.
 */
struct SIContract {
    bytes32 hash;
    address submitter;
    uint status;
}

/**
 * Mapping for contract registry.
 */
mapping(bytes32 => SIContract) public sicontracts;

/**
 * The only permission worth setting; doing the reverse is pointless as a contract
 * owner can interact with the contract as an anonymous third party simply by using
 * another public key address.
 */
modifier onlyBy(address _account) {
    if (msg.sender != _account) {
        revert();
    }
    _;
}

/**
 * Anyone can submit a contract for acceptance into a registry.
 */
function submitContract(bytes32 _contractHash, address idOwner) returns(bool) {
    var sicontract = sicontracts[_contractHash];
    sicontract.hash = _contractHash;
    sicontract.submitter = idOwner;
    sicontract.status = PENDING;
    return true;
}

/**
 * Only the registry owner (ideally multi-sig) can approve a contract.
 */
function approveContract(bytes32 _contractHash) returns(bool) {
    var sicontract = sicontracts[_contractHash];
    if(sicontract.submitter != msg.sender){
        return false;
    }
    sicontract.status = ACTIVE;
    return true;
}

/**
 * Only the registry owner (ideally multi-sig) can reject a contract.
 */
function rejectContract(bytes32 _contractHash) onlyBy(owner) returns(bool) {
    var sicontract = sicontracts[_contractHash];
    sicontract.status = REJECTED;
    return true;
}

/**
 * Only the registry owner and original submitter can delete a contract.
 * A contract in the rejected list cannot be removed.
 */
function deleteContract(bytes32 _contractHash) returns(bool) {
    var sicontract = sicontracts[_contractHash];
    if (sicontract.status != REJECTED) {
        if (sicontract.submitter == msg.sender) {
            if (msg.sender == owner) {
                delete sicontracts[_contractHash];
                return true;
            }
        }
    } else {
        revert();
    }
}

/**
 * This is the public registry function that contracts should use to check
 * whether a contract is valid. It's defined as a function, rather than .call
 * so that the registry owner can choose to charge based on their reputation
 * of managing good contracts in a registry.
 *
 * Using a function rather than a call also allows for better management of
 * dependencies when a chain forks, as the registry owner can choose to kill
 * the registry on the wrong fork to stop this function executing.
 */
function isValidContract(bytes32 _contractHash) returns(bool) {
    if (sicontracts[_contractHash].status == ACTIVE) {
        return true;
    }
    if (sicontracts[_contractHash].status == REJECTED) {
        revert();
    } else {
        return false;
    }
}
}

Python code of calling submit function

 self.smartIdContract.call({"from": sender_account}).submitContract(hex_hash,decode_hex(id_public_key))

Code for verifying above submitted entry

self.smartIdContract.call({"from": wrong_addr}).approveContract(id_hash)

What am I doing wrong ? Why my mocha test cases work fine and web3.py calls fails ? I am using testrpc as network.

My mocha test cases for the above contract is :

/**
 * The purpose of this test contract is to test the functions in      SmartIdentityRegistry.sol.
 */

var SmartIdentityRegistry = artifacts.require("SmartIdentityRegistry");

contract('SmartIdentityRegistry', function(accounts) {

var registry,
    contractRegistry1,
    contractRegistry2,
    contracthash1,
    contracthash2;

contracthash1 = '0xca02b2202ffaacbd499438ef6d594a48f7a7631b60405ec8f30a0d7c096d54d5';
contracthash2 = '0xca02b2202ffaacbd499438ef6d594a48f7a7631b60405ec8f30a0d7c096dc3ff';

before("Setup the Smart Identity registry and hydrate the required variables", function(done) {
    contractRegistry1 = accounts[0];
    contractRegistry2 = accounts[1];

    SmartIdentityRegistry.new({from: contractRegistry1})
    .then(function(response) {
        registry = response;
        done();
    });

    return registry,
    contractRegistry1,
    contractRegistry2;
});

describe("SmartIdentityRegistry tests", function() {

    it("will submit a contract into the registry", function() {
        return registry.submitContract(contracthash1,contractRegistry2, {from: contractRegistry1})
        .then(function(response) {
            assert.isOk(response, 'Contract submitting failed');
        });
    });

    it("will prove that the submitter can only  approve a contract", function() {
        return registry.approveContract(contracthash1, {from: contractRegistry2})
        .then(function(response) {
            assert.isOk(response, 'Contract approval failed');
        });
    });


    it("will prove that a non-id owner cannot approve a contract", function() {
        return registry.approveContract(contracthash1, {from: contractRegistry1})
        .catch(function(error) {
            assert.isOk(error, "Expected error has not been caught");
        });
    });

    it("will prove that the registry owner can reject a contract", function() {
        return registry.rejectContract(contracthash1, {from: contractRegistry1})
        .then(function(response) {
            assert.isOk(response, 'Contract rejection failed');
        });
    });

    it("will prove that a non-owner cannot reject a contract", function() {
        return registry.rejectContract(contracthash1, {from: contractRegistry2})
        .catch(function(error) {
            assert.isOk(error, "Expected error has not been caught");
        });
    });

    it("will delete a contract from the registry", function() {
        registry.submitContract(contracthash2,contractRegistry2, {from: contractRegistry1})
        .then(function(response) {
            assert.isOk(response, 'Contract submitting failed');
        });
        return registry.deleteContract(contracthash2).then(function(response) {
            assert.isOk(response, 'Contract failed to be deleted');
        });
    });

    it("will verify a contract is not valid", function() {
        registry.submitContract(contracthash2,contractRegistry2, {from: contractRegistry1})
        .then(function(response) {
            assert.isOk(response, 'Contract submitting failed');
        });
        return registry.isValidContract(contracthash2).then(function(response) {
            assert.isOk(response, 'Failed to verify contract');
        });
    });

    it("will verify a contract is not valid and will throw an error", function() {
        registry.submitContract(contracthash2,contractRegistry2, {from: contractRegistry1})
        .then(function(response) {
            assert.isOk(response, 'Contract submitting failed');
        });
        registry.rejectContract(contracthash2, {from: contractRegistry1});
        return registry.isValidContract(contracthash2)
        .catch(function(error) {
            assert.isOk(error, "Expected error has not been caught");
        });
    });

    it("will verify a contract is valid", function() {
        registry.submitContract(contracthash2, contractRegistry2,{from: contractRegistry1})
        .then(function(response) {
            assert.isOk(response, 'Contract submitting failed');
        });
        registry.approveContract(contracthash2, {from: contractRegistry2});
        return registry.isValidContract(contracthash2)
        .then(function(response) {
            assert.isOk(response, 'Failed to verify contract');
        });
    });
});

 });
1

Instead of a call, you want a transaction. See: What is the difference between a transaction and a call?

So instead of:

self.smartIdContract.call({"from": sender_account}).submitContract(...)

Try:

self.smartIdContract.transact({"from": sender_account}).submitContract(...)

Or the newer syntax from Web3.py v4:

bound_submission = self.smartIdContract.functions.submitContract(...)
txn_hash = bound_submission.transact({"from": sender_account})
  • Thanks Carver. How will I get the result then in the second verification ? Should I use event or can I have this without changing above contract ? – Bill Goldberg Nov 15 '17 at 20:11
  • Or can I make call and say it is verified ? – Bill Goldberg Nov 15 '17 at 20:47
  • After your second transaction, you could wait for it to be mined and then call isValidContract. But yes, an event might be even better here. – carver Nov 16 '17 at 2:49
  • This is a web app user first submit id hash and verifies with approving after that. 2 consecutive steps. How can I wait untill mined ? Is there blocking call ? My plan is. As follows first transact on submit contract and call on second function. – Bill Goldberg Nov 16 '17 at 2:53
  • Waiting for the event sounds like your best option. – carver Nov 16 '17 at 4:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.