Author

Topic: multisig transaction with bitcoinj (Read 1671 times)

newbie
Activity: 1
Merit: 0
September 04, 2015, 06:11:05 AM
#1
Hi guys, I am having issues setting up a multisig transaction with bitcoinj on my regtest network. I am running the latest bitcoinj lib. Please see the code below. The program is a 2 of 2 multisig payment. I use the WalletAppkit. The whole flow of the app is
- I have 3 parties sender, intermediary, and receiver.
- Intermediary and receiver send pubkey to sender.
- Sender get two public keys from other two users on the regtest network, they are hard coded into the program for test convenience.
- Sender forms a multisig transaction with the pubkey of the other two users, then broadcasts.
- Intermediary gets the transaction, signs it and sends its signature to receiver
- Receiver gets the sig of intermediary, signs the transaction with it's sig and intermediary's
- Then it shows money has been gotten

Problem
- let say I have 5btc in the sender wallet, I then send 1btc to the receiver. On the receiver side instead of 1btc being put in the wallet I get the 3.99btc (that is the receiver gets the balance of the sender after mining fees). It's rather a strange behavior.
- secondly it works sometimes the correct amount gets sent to the receiver but I am not able to send the amount.

Thank you for your anticipated help.

Please see my code below
Code:

//....relevant imports

public class Bitsender {

    NetworkParameters params = RegTestParams.get();
    WalletAppKit kit = new WalletAppKit(params, new File("."),"sender-wallet" );
    
    public void startUp(){
        System.out.println("Setting things up..");
        
        kit.connectToLocalHost();
        kit.startAsync();
        kit.awaitRunning();
        WalletListener wlistener = new WalletListener();
        kit.wallet().addEventListener(wlistener);
    }
    
    private void receive() {
        System.out.println("Feed me: " + kit.wallet().freshReceiveAddress().toString());
    }
    
    public void getBal(){
        System.out.println("Sender Balance is " + kit.wallet().getBalance());
    }
    
    private void send(String address) throws AddressFormatException, InsufficientMoneyException {
      
        //this are the byte rep of the two multisig keys
        byte[] reckey = new byte[]{2,127,-81,126,111,23,-36,124,-79,-15,83,56,-52,18,58,-80,-100,-120,9,57,-98,99,-96,29,10,-19,-74,-84,-32,43,19,9,111};
        byte[] intkey = new byte[]{3,-27,73,-95,-86,-98,70,-51,-34,99,53,8,-5,127,59,-22,-124,-76,-112,49,-62,61,-51,-42,107,88,-26,-100,-23,62,-118,-41,80};
        //convert them to pubkyes
        ECKey receiverkey = new ECKey(null, reckey);
        ECKey intermediarykey = new ECKey(null, intkey);
        //create a transaction
        Transaction contract = new Transaction(params);
        //form a list of keys to be added to multisig script
        List keys  = ImmutableList.of(receiverkey, intermediarykey);
        //construct script with keys
        Script script = ScriptBuilder.createMultiSigOutputScript(2, keys);
        //set amount to be sent
        Coin amount = Coin.parseCoin("10");//.valueOf(0, 80);
        //form an output and add the amount and script
        contract.addOutput(amount, script);
        System.out.println("Value to be sent " +contract.getOutput(0).getValue());
        //adding input to the transaction from the wallet
        
        Wallet.SendRequest req = Wallet.SendRequest.forTx(contract);
        kit.wallet().completeTx(req);
        kit.wallet().signTransaction(req);

        //kit.wallet().sendCoins(req);
        
        String sscript = new Gson().toJson(script);
        System.out.println("Script for this transaction is-----" + "\n" + sscript);
        System.out.println("End of script");
        
        byte[] trx = contract.bitcoinSerialize();
        String strx = new Gson().toJson(trx);
        System.out.println("Transaction is-----" + "\n" + strx);
        System.out.println("End of transaction");
        kit.peerGroup().broadcastTransaction(req.tx);
    }
    
   /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException, AddressFormatException, InsufficientMoneyException {
        System.out.println("Sender Client");
        String userInput = "";
        Bitsender sender = new Bitsender();
        sender.startUp();
        
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        while (!"quit".equals(userInput = in.readLine())) {
            if(userInput.equals("receive")){
                sender.receive();
            }
            else if(userInput.equals("bal")){
                sender.getBal();
            }
            else if(userInput.equals("send")){
                //get the address of the bitreceiver
                sender.send(userInput);
                //sender.send(userInput.split(" ")[1]);
            }            
        }
    }
    
}

//...relevant imports
public class Bitreceiver {

    NetworkParameters params = RegTestParams.get();
    WalletAppKit kit = new WalletAppKit(params, new File("."),"receiver-wallet" );
    
    public void startUp(){
        System.out.println("Setting things up..");
        
        //kit.connectToLocalHost();
        kit.startAsync();
        kit.awaitRunning();
        WalletListener wlistener = new WalletListener();
        kit.wallet().addEventListener(wlistener);
    }
    
    public void getBal(){
        System.out.println("Balance is " + kit.wallet().getBalance());
    }
        
    private void receive() {
        //System.out.println("Feed me: " + kit.wallet().getActiveKeychain().getKey(KeyChain.KeyPurpose.RECEIVE_FUNDS).toAddress(params));
        ECKey key = kit.wallet().freshReceiveKey();//.freshReceiveAddress();
        Address addr = key.toAddress(params);
        byte[] pubkey = key.getPubKey();
        String spk = new Gson().toJson(pubkey);
        System.out.println("Feed me address: " + addr.toString());
        System.out.println("Feed me public key: " + spk);        
    }
    
    private void claim(){
        //System.out.println(kit.wallet().getWalletTransactions().toString());
        byte[] reckey = new byte[]{2,127,-81,126,111,23,-36,124,-79,-15,83,56,-52,18,58,-80,-100,-120,9,57,-98,99,-96,29,10,-19,-74,-84,-32,43,19,9,111};
        byte[] intkey = new byte[]{3,-27,73,-95,-86,-98,70,-51,-34,99,53,8,-5,127,59,-22,-124,-76,-112,49,-62,61,-51,-42,107,88,-26,-100,-23,62,-118,-41,80};
        
        String sint = "{\"r\":95659142858888513941404554021932981678862401841800158047860591604698882661844,\"s\":44245425850115539565526070211043699757053575051384836017709636920266660223817}";
        ECKey.ECDSASignature intsig = new Gson().fromJson(sint, ECKey.ECDSASignature.class);
        
        ECKey receiverkey = kit.wallet().getActiveKeychain().findKeyFromPubKey(reckey);
        
        byte[] contractbyte = new byte[]{1,0,0,0,1,-65,50,31,-50,1,92,17,17,116,-30,-68,80,43,-114,-56,89,116,-58,-65,-46,-111,6,-102,-43,-69,82,41,-46,7,-6,-94,75,1,0,0,0,106,71,48,68,2,32,70,5,-15,48,-90,-94,-56,-21,90,-23,97,65,106,-86,52,-101,-39,34,45,-103,75,35,123,65,4,-64,24,97,-47,-55,-95,50,2,32,10,48,-59,-72,31,-17,49,-104,-12,-115,96,109,-18,78,82,80,5,-105,33,94,83,51,-41,41,-124,-80,-76,-40,11,-35,0,59,1,33,3,-121,-54,-90,-3,-49,-25,21,-89,4,-19,111,-125,-12,-76,4,71,90,-117,-102,92,104,-103,109,-98,3,-49,100,23,125,102,-54,-120,-1,-1,-1,-1,2,0,-54,-102,59,0,0,0,0,71,82,33,2,127,-81,126,111,23,-36,124,-79,-15,83,56,-52,18,58,-80,-100,-120,9,57,-98,99,-96,29,10,-19,-74,-84,-32,43,19,9,111,33,3,-27,73,-95,-86,-98,70,-51,-34,99,53,8,-5,127,59,-22,-124,-76,-112,49,-62,61,-51,-42,107,88,-26,-100,-23,62,-118,-41,80,82,-82,96,85,-51,29,0,0,0,0,25,118,-87,20,-24,119,-65,42,-114,-21,-75,-74,-64,83,-94,31,-104,-33,-59,47,9,42,112,-95,-120,-84,0,0,0,0};
        
        Transaction contract = new Transaction(params, contractbyte);
        contract.verify();
        TransactionOutput multisigOutput = contract.getOutput(1);
        Script multisigScript = multisigOutput.getScriptPubKey();
        Coin value = multisigOutput.getValue();
        //byte[] contractbyte = new byte[]{1,0,0,0,1,75,-53,74,65,97,96,55,-110,-23,-44,-84,93,-111,-92,-107,-110,116,7,-88,32,90,-73,110,116,-2,25,-125,8,-52,-75,58,74,1,0,0,0,107,72,48,69,2,33,0,-31,-42,-97,41,115,46,-62,-78,116,80,100,17,0,121,-9,13,72,-67,9,28,72,75,-7,27,-7,-90,95,10,-11,8,-25,104,2,32,118,-15,48,-79,80,-83,-33,-115,-45,115,101,-42,105,39,96,-91,73,-2,44,51,-116,12,111,120,-104,-80,-44,97,108,62,-91,68,1,33,3,-88,94,-90,38,-41,-54,56,127,83,-118,7,15,112,98,84,-58,109,-100,-111,-90,23,-109,50,23,-108,85,-75,30,19,99,-110,-57,-1,-1,-1,-1,2,-104,-115,-46,-19,0,0,0,0,25,118,-87,20,101,93,-123,-49,-47,-23,2,-92,-22,-91,51,-49,-23,52,101,-28,-85,-13,-46,-18,-120,-84,-128,-106,-104,0,0,0,0,0,71,82,33,2,41,20,-11,-125,93,119,-120,69,101,101,11,-13,114,13,9,33,-103,-84,-127,-30,67,-74,42,-124,-120,109,33,-48,-128,27,-116,-86,33,2,-89,-31,-23,-13,-52,-51,48,-28,-45,13,-90,-105,49,104,-112,-10,-128,-72,-34,12,19,47,-9,-54,-11,98,-100,126,-21,44,38,109,82,-82,0,0,0,0};
        System.out.println("Value sent " + value.value);
        
        Transaction spendtx = new Transaction(params);
        spendtx.addOutput(value, receiverkey);
        TransactionInput input = spendtx.addInput(multisigOutput);
        Sha256Hash sighash = spendtx.hashForSignature(0, multisigScript, Transaction.SigHash.ALL, false);
        ECKey.ECDSASignature recsig = receiverkey.sign(sighash);
         //ECKey.ECDSASignature.decodeFromDER();
        //List keys  = ImmutableList.of(recsig, intsig);
        
        TransactionSignature tsrecsig = new TransactionSignature(recsig, Transaction.SigHash.ALL, false);
        TransactionSignature tsintsig = new TransactionSignature(intsig, Transaction.SigHash.ALL, false);
        
        Script inputScript = ScriptBuilder.createMultiSigInputScript(ImmutableList.of(tsrecsig, tsintsig));
        
        input.setScriptSig(inputScript);
        
        System.out.println(new Gson().toJson(multisigScript.toString()));
        //System.out.println(multisigOutput.getValue());
        
        //input.verify(multisigOutput);
        
        kit.peerGroup().broadcastTransaction(spendtx);
        System.out.println(kit.wallet().getBalance());
        kit.wallet().allowSpendingUnconfirmedTransactions();
    }
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException, AddressFormatException, InsufficientMoneyException {
        System.out.println("Receiver Client");
        String userInput = "";
        Bitreceiver receiver = new Bitreceiver();
        receiver.startUp();
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        while (!"quit".equals(userInput = in.readLine())) {
            if(userInput.equals("bal")){
                receiver.getBal();
            }
            else if(userInput.equals("receive")){
                receiver.receive();
            }          
            else if(userInput.equals("claim")){
                receiver.claim();
            }
            else if(userInput.equals("send")){
                receiver.send();
            }
        }
    }    
}

//..relevant imports
public class Bitintermediary {

   NetworkParameters params = RegTestParams.get();
   WalletAppKit kit = new WalletAppKit(params, new File("."),"intermediarywallet" );
  
    public void startUp(){
        System.out.println("Setting things up..");
        
        //kit.connectToLocalHost();
        kit.startAsync();
        kit.awaitRunning();
        WalletListener wlistener = new WalletListener();
        kit.wallet().addEventListener(wlistener);
        
        //System.out.println("Send me money on: " + kit.wallet().freshReceiveAddress().toString());
    }
    
    private void receive() {
//        System.out.println("Feed me: " + kit.wallet().freshReceiveAddress().toString());
        ECKey key = kit.wallet().freshReceiveKey();//.freshReceiveAddress();
        Address addr = key.toAddress(params);
        byte[] pubkey = key.getPubKey();
        String spk = new Gson().toJson(pubkey);
        System.out.println("Feed me address: " + addr.toString());
        System.out.println("Feed me public key: " + spk);  
    }
    
    public void getBal(){
        System.out.println("Balance is " + kit.wallet().getBalance());
    }
    
    private void send(String address) throws AddressFormatException, InsufficientMoneyException {
        //Address to = Address.fromBase58(params, address);
        Address to = new Address(params, address);
        Coin value = Coin.parseCoin("10.00");
        kit.wallet().sendCoins(kit.peerGroup(), to, value);
        System.out.println("Sending some coins to: " + address);
    }
    
    private void claim(){
        byte[] reckey = new byte[]{2,127,-81,126,111,23,-36,124,-79,-15,83,56,-52,18,58,-80,-100,-120,9,57,-98,99,-96,29,10,-19,-74,-84,-32,43,19,9,111};
        byte[] intkey = new byte[]{3,-27,73,-95,-86,-98,70,-51,-34,99,53,8,-5,127,59,-22,-124,-76,-112,49,-62,61,-51,-42,107,88,-26,-100,-23,62,-118,-41,80};
        
        
        ECKey interkey = kit.wallet().getActiveKeychain().findKeyFromPubKey(intkey);//new ECKey(null, intkey);
        ECKey receiverkey = new ECKey(null, reckey);
        
        //ECKey jm = kit.wallet().isPubKeyMine(intkey);//.getActiveKeychain().findKeyFromPubKey(intkey);
        //kit.wallet().
        //System.out.println(kit.wallet().isPubKeyMine(intkey));
        System.out.println("Here \n" + kit.wallet().getActiveKeychain().findKeyFromPubKey(intkey).getPrivKey());
        //ECKey key = new ECKey();
//        System.out.println(interkey.getPrivKey());
        byte[] contractbyte = new byte[]{1,0,0,0,1,-65,50,31,-50,1,92,17,17,116,-30,-68,80,43,-114,-56,89,116,-58,-65,-46,-111,6,-102,-43,-69,82,41,-46,7,-6,-94,75,1,0,0,0,106,71,48,68,2,32,70,5,-15,48,-90,-94,-56,-21,90,-23,97,65,106,-86,52,-101,-39,34,45,-103,75,35,123,65,4,-64,24,97,-47,-55,-95,50,2,32,10,48,-59,-72,31,-17,49,-104,-12,-115,96,109,-18,78,82,80,5,-105,33,94,83,51,-41,41,-124,-80,-76,-40,11,-35,0,59,1,33,3,-121,-54,-90,-3,-49,-25,21,-89,4,-19,111,-125,-12,-76,4,71,90,-117,-102,92,104,-103,109,-98,3,-49,100,23,125,102,-54,-120,-1,-1,-1,-1,2,0,-54,-102,59,0,0,0,0,71,82,33,2,127,-81,126,111,23,-36,124,-79,-15,83,56,-52,18,58,-80,-100,-120,9,57,-98,99,-96,29,10,-19,-74,-84,-32,43,19,9,111,33,3,-27,73,-95,-86,-98,70,-51,-34,99,53,8,-5,127,59,-22,-124,-76,-112,49,-62,61,-51,-42,107,88,-26,-100,-23,62,-118,-41,80,82,-82,96,85,-51,29,0,0,0,0,25,118,-87,20,-24,119,-65,42,-114,-21,-75,-74,-64,83,-94,31,-104,-33,-59,47,9,42,112,-95,-120,-84,0,0,0,0};
        Transaction contract = new Transaction(params, contractbyte);
        contract.verify();
        TransactionOutput multisigOutput = contract.getOutput(1);
        Script multisigScript = multisigOutput.getScriptPubKey();
        //is the output what we expected
        //checkState(multisigScript.isSentToMultiSig());
        Coin value = multisigOutput.getValue();
        System.out.println("Value sent " + value.value);
        Transaction spendtx = new Transaction(params);
      
        spendtx.addOutput(value, receiverkey);
        spendtx.addInput(multisigOutput);
        
        //sign and send sig to receiver to claim
        Sha256Hash sighash = spendtx.hashForSignature(0, multisigScript, Transaction.SigHash.ALL, false);
        ECKey.ECDSASignature signature = interkey.sign(sighash);
        System.out.println(signature.toString());
        String sig = new Gson().toJson(signature);
        System.out.println("Signature of intermediary is:\n" + sig);
        System.out.println("End of sig");
    }
    
    public static void main(String[] args) throws IOException, AddressFormatException, InsufficientMoneyException {
        System.out.println("Intermediary Client");
        String userInput = "";
        Bitintermediary inter = new Bitintermediary();
        inter.startUp();
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        while (!"quit".equals(userInput = in.readLine())) {
            if(userInput.equals("receive")){
                inter.receive();
            }
            else if(userInput.equals("bal")){
                inter.getBal();
            }
            else if(userInput.equals("claim")){
                inter.claim();
            }            
        }
    }        
}
Thank you.
Jump to: