function signOne(hash, addrStr, keys)
{
	var keyObj = keys[addrStr];
	var rawPrivKey = new Buffer(keyObj.priv, 'hex');
	var key = new KeyModule.Key();
	key.private = rawPrivKey;
	var signature = key.signSync(hash);

	return signature;
}

function signTxIn(nIn, tx, txInputs, network, keys, scripts)
{
	// locate TX input needing a signature
	var txin = tx.ins[nIn];
	var scriptSig = txin.getScript();

	// locate TX output, within txInputs
	var txoutHash = txin.getOutpointHash();
	if (!(txoutHash in txInputs))
		throw new Error("signTxIn missing input hash");
	var txFrom = txInputs[txoutHash];
	var txoutIndex = txin.getOutpointIndex();
	if (txFrom.outs.length >= txoutIndex)
		throw new Error("signTxIn missing input index");
	var txout = txFrom.outs[txoutIndex];
	var scriptPubKey = txout.getScript();

	// detect type of transaction, and extract useful elements
	var txType = scriptPubKey.classify();
	if (txType == TX_UNKNOWN)
		throw new Error("unknown TX type");
	var scriptData = scriptPubKey.capture();

	// if P2SH, lookup the script
	var subscriptRaw = undefined;
	var subscript = undefined;
	var subType = undefined;
	var subData = undefined;
	if (txType == TX_SCRIPTHASH) {
		var addr = new Address(network.addressScript, scriptData[0]);
		var addrStr = addr.toString();
		if (!(addrStr in scripts))
			throw new Error("unknown script hash address");

		subscriptRaw = new Buffer(scripts[addrStr], 'hex');
		subscript = new Script(subscriptRaw);
		subType = subscript.classify();
		if (subType == TX_UNKNOWN)
			throw new Error("unknown subscript TX type");
		subData = subscript.capture();
	}

	var hash = tx.hashForSignature(scriptPubKey, i, 0);

	switch (txType) {
	case TX_PUBKEY:
		// already signed
		if (scriptSig.chunks.length > 0)
			return;

		var pubkeyhash = util.sha256ripe160(scriptData[0]);
		var addr = new Address(network.addressPubkey, pubkeyhash);
		var addrStr = addr.toString();
		if (!(addrStr in keys))
			throw new Error("unknown pubkey");

		var signature = signOne(hash, addrStr, keys);
		scriptSig.writeBytes(signature);
		break;

	case TX_PUBKEYHASH:
		// already signed
		if (scriptSig.chunks.length > 0)
			return;

		var addr = new Address(network.addressPubkey, scriptData[0]);
		var addrStr = addr.toString();
		if (!(addrStr in keys))
			throw new Error("unknown pubkey hash address");

		var signature = signOne(hash, addrStr, keys);
		scriptSig.writeBytes(signature);
		scriptSig.writeBytes(key.public);
		break;

	case TX_SCRIPTHASH:
		// already signed
		if (scriptSig.chunks.length > 0)
			return;

		var addr = new Address(network.addressPubkey, subData[0]);
		var addrStr = addr.toString();
		if (!(addrStr in keys))
			throw new Error("unknown script(pubkey hash) address");

		var signature = signOne(hash, addrStr, keys);
		scriptSig.writeBytes(signature);
		scriptSig.writeBytes(key.public);
		break;

	case TX_MULTISIG:
		while (scriptSig.chunks.length < scriptData.length) {
			scriptSig.writeBytes(util.EMPTY_BUFFER);
		}
		for (var i = 0; i < scriptData.length; i++) {
			// skip already signed
			if (scriptSig.chunks[i].length > 0)
				continue;

			var pubkeyhash = util.sha256ripe160(scriptSig.chunks[i]);
			var addr = new Address(network.addressPubkey, pubkeyhash);
			var addrStr = addr.toString();
			if (!(addrStr in keys))
				continue;

			var signature = signOne(hash, addrStr, keys);
			scriptSig.chunks[i] = signature;
		}
		break;
	}

	if (txtype == TX_SCRIPTHASH)
		scriptSig.writeBytes(subscriptRaw);
}

exports.Transaction = function Transaction(tx, txInputs, network, keys, scripts)
{
	for (var i = 0; i < tx.ins.length; i++)
		signTxIn(i, tx, txInputs, network, keys, scripts);
};