<commit_before>'use strict';

var d3 = require('@plotly/d3');

var helpers = require('../sunburst/helpers');

var uniformText = require('../bar/uniform_text');
var clearMinTextSize = uniformText.clearMinTextSize;
var resizeText = require('../bar/style').resizeText;
var drawDescendants = require('./draw_descendants');
var plotOne = require('./plot_one');
var type = 'treemap';

module.exports = function _plot(gd, cdmodule, transitionOpts, makeOnCompleteCallback) {
    var fullLayout = gd._fullLayout;
    var layer = fullLayout['_' + type + 'layer'];
    var join, onComplete;

    // If transition config is provided, then it is only a partial replot and traces not
    // updated are removed.
    var isFullReplot = !transitionOpts;

    clearMinTextSize(type, fullLayout);

    join = layer.selectAll('g.trace.' + type)
        .data(cdmodule, function(cd) { return cd[0].trace.uid; });

    join.enter().append('g')
        .classed('trace', true)
        .classed(type, true);

    join.order();

    if(!fullLayout.uniformtext.mode && helpers.hasTransition(transitionOpts)) {
        if(makeOnCompleteCallback) {
            // If it was passed a callback to register completion, make a callback. If
            // this is created, then it must be executed on completion, otherwise the
            // pos-transition redraw will not execute:
            onComplete = makeOnCompleteCallback();
        }

        var transition = d3.transition()
            .duration(transitionOpts.duration)
            .ease(transitionOpts.easing)
            .each('end', function() { onComplete && onComplete(); })
            .each('interrupt', function() { onComplete && onComplete(); });

        transition.each(function() {
            // Must run the selection again since otherwise enters/updates get grouped together
            // and these get executed out of order. Except we need them in order!
            layer.selectAll('g.trace').each(function(cd) {
                plotOne(gd, cd, this, transitionOpts, drawDescendants);
            });
        });
    } else {
        join.each(function(cd) {
            plotOne(gd, cd, this, transitionOpts, drawDescendants);
        });

        if(fullLayout.uniformtext.mode) {
            resizeText(gd, layer.selectAll('.trace'), type);
        }
    }

    if(isFullReplot) {
        join.exit().remove();
    }
};

function getKey(pt) {
    return helpers.isHierarchyRoot(pt) ?
        '' : // don't use the dummyId
        helpers.getPtId(pt);
}

function plotOne(gd, cd, element, transitionOpts) {
    var fullLayout = gd._fullLayout;
    var cd0 = cd[0];
    var trace = cd0.trace;
    var type = trace.type;
    var hierarchy = cd0.hierarchy;
    var entry = helpers.findEntryWithLevel(hierarchy, trace.level);

    var gTrace = d3.select(element);
    var selAncestors = gTrace.selectAll('g.pathbar');
    var selDescendants = gTrace.selectAll('g.slice');

    if(!entry) {
        selAncestors.remove();
        selDescendants.remove();
        return;
    }

    var isRoot = helpers.isHierarchyRoot(entry);
    var hasTransition = !fullLayout.uniformtext.mode && helpers.hasTransition(transitionOpts);

    var maxDepth = helpers.getMaxDepth(trace);
    var hasVisibleDepth = function(pt) {
        return pt.data.depth - entry.data.depth < maxDepth;
    };

    var gs = fullLayout._size;
    var domain = trace.domain;

    var vpw = gs.w * (domain.x[1] - domain.x[0]);
    var vph = gs.h * (domain.y[1] - domain.y[0]);
    var barW = vpw;
    var barH = trace.pathbar.thickness;
    var barPad = trace.marker.line.width + constants.gapWithPathbar;
    var barDifY = !trace.pathbar.visible ? 0 :
        trace.pathbar.side.indexOf('bottom') > -1 ? vph + barPad : -(barH + barPad);

    var pathbarOrigin = {
        x0: barW, // slide to the right
        x1: barW,
        y0: barDifY,
        y1: barDifY + barH
    };

    var findClosestEdge = function(pt, ref, size) {
        var e = trace.tiling.pad;
        var isLeftOfRect = function(x) { return x - e <= ref.x0; };
        var isRightOfRect = function(x) { return x + e >= ref.x1; };
        var isBottomOfRect = function(y) { return y - e <= ref.y0; };
        var isTopOfRect = function(y) { return y + e >= ref.y1; };

        return {
            x0: isLeftOfRect(pt.x0 - e) ? 0 : isRightOfRect(pt.x0 - e) ? size[0] : pt.x0,
            x1: isLeftOfRect(pt.x1 + e) ? 0 : isRightOfRect(pt.x1 + e) ? size[0] : pt.x1,
            y0: isBottomOfRect(pt.y0 - e) ? 0 : isTopOfRect(pt.y0 - e) ? size[1] : pt.y0,
            y1: isBottomOfRect(pt.y1 + e) ? 0 : isTopOfRect(pt.y1 + e) ? size[1] : pt.y1
        };
    };

    // stash of 'previous' position data used by tweening functions
    var prevEntry = null;
    var prevLookupPathbar = {};
    var prevLookupSlices = {};
    var nextOfPrevEntry = null;
    var getPrev = function(pt, onPathbar) {
        return onPathbar ?
            prevLookupPathbar[getKey(pt)] :
            prevLookupSlices[getKey(pt)];
    };

    var getOrigin = function(pt, onPathbar, refRect, size) {
        if(onPathbar) {
            return prevLookupPathbar[getKey(hierarchy)] || pathbarOrigin;
        } else {
            var ref = prevLookupSlices[trace.level] || refRect;

            if(hasVisibleDepth(pt)) { // case of an empty object - happens when maxdepth is set
                return findClosestEdge(pt, ref, size);
            }
        }
        return {};
    };

    // N.B. handle multiple-root special case
    if(cd0.hasMultipleRoots && isRoot) {
        maxDepth++;
    }

    trace._maxDepth = maxDepth;
    trace._backgroundColor = fullLayout.paper_bgcolor;
    trace._entryDepth = entry.data.depth;
    trace._atRootLevel = isRoot;

    var cenX = -vpw / 2 + gs.l + gs.w * (domain.x[1] + domain.x[0]) / 2;
    var cenY = -vph / 2 + gs.t + gs.h * (1 - (domain.y[1] + domain.y[0]) / 2);

    var viewMapX = function(x) { return cenX + x; };
    var viewMapY = function(y) { return cenY + y; };

    var barY0 = viewMapY(0);
    var barX0 = viewMapX(0);

    var viewBarX = function(x) { return barX0 + x; };
    var viewBarY = function(y) { return barY0 + y; };

    function pos(x, y) {
        return x + ',' + y;
    }

    var xStart = viewBarX(0);
    var limitX0 = function(p) { p.x = Math.max(xStart, p.x); };

    var edgeshape = trace.pathbar.edgeshape;

    // pathbar(directory) path generation fn
    var pathAncestor = function(d) {
        var _x0 = viewBarX(Math.max(Math.min(d.x0, d.x0), 0));
        var _x1 = viewBarX(Math.min(Math.max(d.x1, d.x1), barW));
        var _y0 = viewBarY(d.y0);
        var _y1 = viewBarY(d.y1);

        var halfH = barH / 2;

        var pL = {};
        var pR = {};

        pL.x = _x0;
        pR.x = _x1;

        pL.y = pR.y = (_y0 + _y1) / 2;

        var pA = {x: _x0, y: _y0};
        var pB = {x: _x1, y: _y0};
        var pC = {x: _x1, y: _y1};
        var pD = {x: _x0, y: _y1};

        if(edgeshape === '>') {
            pA.x -= halfH;
            pB.x -= halfH;
            pC.x -= halfH;
            pD.x -= halfH;
        } else if(edgeshape === '/') {
            pC.x -= halfH;
            pD.x -= halfH;
            pL.x -= halfH / 2;
            pR.x -= halfH / 2;
        } else if(edgeshape === '\\') {
            pA.x -= halfH;
            pB.x -= halfH;
            pL.x -= halfH / 2;
            pR.x -= halfH / 2;
        } else if(edgeshape === '<') {
            pL.x -= halfH;
            pR.x -= halfH;
        }

        limitX0(pA);
        limitX0(pD);
        limitX0(pL);

        limitX0(pB);
        limitX0(pC);
        limitX0(pR);

        return (
           'M' + pos(pA.x, pA.y) +
           'L' + pos(pB.x, pB.y) +
           'L' + pos(pR.x, pR.y) +
           'L' + pos(pC.x, pC.y) +
           'L' + pos(pD.x, pD.y) +
           'L' + pos(pL.x, pL.y) +
           'Z'
        );
    };

    // slice path generation fn
    var pathDescendant = function(d) {
        var _x0 = viewMapX(d.x0);
        var _x1 = viewMapX(d.x1);
        var _y0 = viewMapY(d.y0);
        var _y1 = viewMapY(d.y1);

        var dx = _x1 - _x0;
        var dy = _y1 - _y0;
        if(!dx || !dy) return '';

        var FILLET = 0; // TODO: may expose this constant

        var r = (
            dx > 2 * FILLET &&
            dy > 2 * FILLET
        ) ? FILLET : 0;

        var arc = function(rx, ry) { return r ? 'a' + pos(r, r) + ' 0 0 1 ' + pos(rx, ry) : ''; };

        return (
           'M' + pos(_x0, _y0 + r) +
           arc(r, -r) +
           'L' + pos(_x1 - r, _y0) +
           arc(r, r) +
           'L' + pos(_x1, _y1 - r) +
           arc(-r, r) +
           'L' + pos(_x0 + r, _y1) +
           arc(-r, -r) + 'Z'
        );
    };

    var toMoveInsideSlice = function(pt, opts) {
        var x0 = pt.x0;
        var x1 = pt.x1;
        var y0 = pt.y0;
        var y1 = pt.y1;
        var textBB = pt.textBB;

        var hasFlag = function(f) { return trace.textposition.indexOf(f) !== -1; };

        var hasBottom = hasFlag('bottom');
        var hasTop = hasFlag('top') || (opts.isHeader && !hasBottom);

        var anchor =
            hasTop ? 'start' :
            hasBottom ? 'end' : 'middle';

        var hasRight = hasFlag('right');
        var hasLeft = hasFlag('left') || opts.onPathbar;

        var leftToRight =
            hasLeft ? -1 :
            hasRight ? 1 : 0;

        var pad = trace[type === 'icicle' ? 'tiling' : 'marker'].pad;
        if(opts.isHeader) {
            x0 += pad.l - TEXTPAD;
            x1 -= pad.r - TEXTPAD;
            if(x0 >= x1) {
                var mid = (x0 + x1) / 2;
                x0 = mid;
                x1 = mid;
            }

            // limit the drawing area for headers
            var limY;
            if(hasBottom) {
                limY = y1 - pad.b;
                if(y0 < limY && limY < y1) y0 = limY;
            } else {
                limY = y0 + pad.t;
                if(y0 < limY && limY < y1) y1 = limY;
            }
        }

        // position the text relative to the slice
        var transform = toMoveInsideBar(x0, x1, y0, y1, textBB, {
            isHorizontal: false,
            constrained: true,
            angle: 0,
            anchor: anchor,
            leftToRight: leftToRight
        });
        transform.fontSize = opts.fontSize;

        transform.targetX = viewMapX(transform.targetX);
        transform.targetY = viewMapY(transform.targetY);

        if(isNaN(transform.targetX) || isNaN(transform.targetY)) {
            return {};
        }

        if(x0 !== x1 && y0 !== y1) {
            recordMinTextSize(trace.type, transform, fullLayout);
        }

        return {
            scale: transform.scale,
            rotate: transform.rotate,
            textX: transform.textX,
            textY: transform.textY,
            anchorX: transform.anchorX,
            anchorY: transform.anchorY,
            targetX: transform.targetX,
            targetY: transform.targetY
        };
    };

    var interpFromParent = function(pt, onPathbar) {
        var parentPrev;
        var i = 0;
        var Q = pt;
        while(!parentPrev && i < maxDepth) { // loop to find a parent/grandParent on the previous graph
            i++;
            Q = Q.parent;
            if(Q) {
                parentPrev = getPrev(Q, onPathbar);
            } else i = maxDepth;
        }
        return parentPrev || {};
    };

    var makeExitSliceInterpolator = function(pt, onPathbar, refRect, size) {
        var prev = getPrev(pt, onPathbar);
        var next;

        if(onPathbar) {
            next = pathbarOrigin;
        } else {
            var entryPrev = getPrev(entry, onPathbar);
            if(entryPrev) {
                // 'entryPrev' is here has the previous coordinates of the entry
                // node, which corresponds to the last "clicked" node when zooming in
                next = findClosestEdge(pt, entryPrev, size);
            } else {
                // this happens when maxdepth is set, when leaves must
                // be removed and the entry is new (i.e. does not have a 'prev' object)
                next = {};
            }
        }

        return d3.interpolate(prev, next);
    };

    var makeUpdateSliceInterpolator = function(pt, onPathbar, refRect, size) {
        var prev0 = getPrev(pt, onPathbar);
        var prev;

        if(prev0) {
            // if pt already on graph, this is easy
            prev = prev0;
        } else {
            // for new pts:
            if(onPathbar) {
                prev = pathbarOrigin;
            } else {
                if(prevEntry) {
                    // if trace was visible before
                    if(pt.parent) {
                        var ref = nextOfPrevEntry || refRect;

                        if(ref && !onPathbar) {
                            prev = findClosestEdge(pt, ref, size);
                        } else {
                            // if new leaf (when maxdepth is set),
                            // grow it from its parent node
                            prev = {};
                            Lib.extendFlat(prev, interpFromParent(pt, onPathbar));
                        }
                    } else {
                        prev = pt;
                    }
                } else {
                    prev = {};
                }
            }
        }

        return d3.interpolate(prev, {
            x0: pt.x0,
            x1: pt.x1,
            y0: pt.y0,
            y1: pt.y1
        });
    };

    var makeUpdateTextInterpolator = function(pt, onPathbar, refRect, size) {
        var prev0 = getPrev(pt, onPathbar);
        var prev = {};
        var origin = getOrigin(pt, onPathbar, refRect, size);

        Lib.extendFlat(prev, {
            transform: toMoveInsideSlice({
                x0: origin.x0,
                x1: origin.x1,
                y0: origin.y0,
                y1: origin.y1,
                textBB: pt.textBB,
                _text: pt._text
            }, {
                isHeader: helpers.isHeader(pt, trace)
            })
        });

        if(prev0) {
            // if pt already on graph, this is easy
            prev = prev0;
        } else {
            // for new pts:
            if(pt.parent) {
                Lib.extendFlat(prev, interpFromParent(pt, onPathbar));
            }
        }

        var transform = pt.transform;
        if(pt.x0 !== pt.x1 && pt.y0 !== pt.y1) {
            recordMinTextSize(trace.type, transform, fullLayout);
        }

        return d3.interpolate(prev, {
            transform: {
                scale: transform.scale,
                rotate: transform.rotate,
                textX: transform.textX,
                textY: transform.textY,
                anchorX: transform.anchorX,
                anchorY: transform.anchorY,
                targetX: transform.targetX,
                targetY: transform.targetY
            }
        });
    };

    var handleSlicesExit = function(slices, onPathbar, refRect, size, pathSlice) {
        var width = size[0];
        var height = size[1];

        if(hasTransition) {
            slices.exit().transition()
                .each(function() {
                    var sliceTop = d3.select(this);

                    var slicePath = sliceTop.select('path.surface');
                    slicePath.transition().attrTween('d', function(pt2) {
                        var interp = makeExitSliceInterpolator(pt2, onPathbar, refRect, [width, height]);
                        return function(t) { return pathSlice(interp(t)); };
                    });

                    var sliceTextGroup = sliceTop.select('g.slicetext');
                    sliceTextGroup.attr('opacity', 0);
                })
                .remove();
        } else {
            slices.exit().remove();
        }
    };

    var strTransform = function(d) {
        var transform = d.transform;

        if(d.x0 !== d.x1 && d.y0 !== d.y1) {
            recordMinTextSize(trace.type, transform, fullLayout);
        }

        return Lib.getTextTransform({
            textX: transform.textX,
            textY: transform.textY,
            anchorX: transform.anchorX,
            anchorY: transform.anchorY,
            targetX: transform.targetX,
            targetY: transform.targetY,
            scale: transform.scale,
            rotate: transform.rotate
        });
    };

    if(hasTransition) {
        // Important: do this before binding new sliceData!

        selAncestors.each(function(pt) {
            prevLookupPathbar[getKey(pt)] = {
                x0: pt.x0,
                x1: pt.x1,
                y0: pt.y0,
                y1: pt.y1
            };

            if(pt.transform) {
                prevLookupPathbar[getKey(pt)].transform = {
                    textX: pt.transform.textX,
                    textY: pt.transform.textY,
                    anchorX: pt.transform.anchorX,
                    anchorY: pt.transform.anchorY,
                    targetX: pt.transform.targetX,
                    targetY: pt.transform.targetY,
                    scale: pt.transform.scale,
                    rotate: pt.transform.rotate
                };
            }
        });

        selDescendants.each(function(pt) {
            prevLookupSlices[getKey(pt)] = {
                x0: pt.x0,
                x1: pt.x1,
                y0: pt.y0,
                y1: pt.y1
            };

            if(pt.transform) {
                prevLookupSlices[getKey(pt)].transform = {
                    textX: pt.transform.textX,
                    textY: pt.transform.textY,
                    anchorX: pt.transform.anchorX,
                    anchorY: pt.transform.anchorY,
                    targetX: pt.transform.targetX,
                    targetY: pt.transform.targetY,
                    scale: pt.transform.scale,
                    rotate: pt.transform.rotate
                };
            }

            if(!prevEntry && helpers.isEntry(pt)) {
                prevEntry = pt;
            }
        });
    }

    var descendantsOpts = {
        width: vpw,
        height: vph,

        viewX: viewMapX,
        viewY: viewMapY,

        pathSlice: pathDescendant,
        toMoveInsideSlice: toMoveInsideSlice,

        prevEntry: prevEntry,
        makeUpdateSliceInterpolator: makeUpdateSliceInterpolator,
        makeUpdateTextInterpolator: makeUpdateTextInterpolator,

        handleSlicesExit: handleSlicesExit,
        hasTransition: hasTransition,
        strTransform: strTransform
    };

    nextOfPrevEntry = type === 'icicle' ?
        drawIcicleGraph(gd, cd, entry, selDescendants, descendantsOpts) :
        drawDescendants(gd, cd, entry, selDescendants, descendantsOpts);

    if(trace.pathbar.visible) {
        drawAncestors(gd, cd, entry, selAncestors, {
            barDifY: barDifY,
            width: barW,
            height: barH,

            viewX: viewBarX,
            viewY: viewBarY,

            pathSlice: pathAncestor,
            toMoveInsideSlice: toMoveInsideSlice,

            makeUpdateSliceInterpolator: makeUpdateSliceInterpolator,
            makeUpdateTextInterpolator: makeUpdateTextInterpolator,

            handleSlicesExit: handleSlicesExit,
            hasTransition: hasTransition,
            strTransform: strTransform
        });
    } else {
        selAncestors.remove();
    }
}
<commit_msg>fix(plot): remove redeclared plotOne() for treemap<commit_after>'use strict';

var d3 = require('@plotly/d3');

var helpers = require('../sunburst/helpers');

var uniformText = require('../bar/uniform_text');
var clearMinTextSize = uniformText.clearMinTextSize;
var resizeText = require('../bar/style').resizeText;
var drawDescendants = require('./draw_descendants');
var plotOne = require('./plot_one');
var type = 'treemap';

module.exports = function _plot(gd, cdmodule, transitionOpts, makeOnCompleteCallback) {
    var fullLayout = gd._fullLayout;
    var layer = fullLayout['_' + type + 'layer'];
    var join, onComplete;

    // If transition config is provided, then it is only a partial replot and traces not
    // updated are removed.
    var isFullReplot = !transitionOpts;

    clearMinTextSize(type, fullLayout);

    join = layer.selectAll('g.trace.' + type)
        .data(cdmodule, function(cd) { return cd[0].trace.uid; });

    join.enter().append('g')
        .classed('trace', true)
        .classed(type, true);

    join.order();

    if(!fullLayout.uniformtext.mode && helpers.hasTransition(transitionOpts)) {
        if(makeOnCompleteCallback) {
            // If it was passed a callback to register completion, make a callback. If
            // this is created, then it must be executed on completion, otherwise the
            // pos-transition redraw will not execute:
            onComplete = makeOnCompleteCallback();
        }

        var transition = d3.transition()
            .duration(transitionOpts.duration)
            .ease(transitionOpts.easing)
            .each('end', function() { onComplete && onComplete(); })
            .each('interrupt', function() { onComplete && onComplete(); });

        transition.each(function() {
            // Must run the selection again since otherwise enters/updates get grouped together
            // and these get executed out of order. Except we need them in order!
            layer.selectAll('g.trace').each(function(cd) {
                plotOne(gd, cd, this, transitionOpts, drawDescendants);
            });
        });
    } else {
        join.each(function(cd) {
            plotOne(gd, cd, this, transitionOpts, drawDescendants);
        });

        if(fullLayout.uniformtext.mode) {
            resizeText(gd, layer.selectAll('.trace'), type);
        }
    }

    if(isFullReplot) {
        join.exit().remove();
    }
};

function getKey(pt) {
    return helpers.isHierarchyRoot(pt) ?
        '' : // don't use the dummyId
        helpers.getPtId(pt);
}
<|endoftext|>


<commit_before>var baddress = require('./address')
var bcrypto = require('./crypto')
var bscript = require('./script')
var bufferEquals = require('buffer-equals')
var bufferReverse = require('buffer-reverse')
var networks = require('./networks')
var ops = require('./opcodes.json')
var typeforce = require('typeforce')
var types = require('./types')

var ECPair = require('./ecpair')
var ECSignature = require('./ecsignature')
var Transaction = require('./transaction')

// re-orders signatures to match pubKeys, fills undefined otherwise
function fixMSSignatures (transaction, vin, pubKeys, signatures, prevOutScript, hashType, skipPubKey) {
  // maintain a local copy of unmatched signatures
  var unmatched = signatures.slice()
  var signatureHash

  return pubKeys.map(function (pubKey) {
    // skip optionally provided pubKey
    if (skipPubKey && bufferEquals(skipPubKey, pubKey)) return undefined

    var matched
    var keyPair2 = ECPair.fromPublicKeyBuffer(pubKey)

    // check for a matching signature
    unmatched.some(function (signature, i) {
      // skip if undefined || OP_0
      if (!signature) return false

      if (!signatureHash) {
        signatureHash = transaction.hashForSignature(vin, prevOutScript, hashType)
      }
      if (!keyPair2.verify(signatureHash, signature)) return false

      // remove matched signature from unmatched
      unmatched[i] = undefined
      matched = signature

      return true
    })

    return matched || undefined
  })
}

function extractInput (transaction, txIn, vin) {
  if (txIn.script.length === 0) return {}

  var scriptSigChunks = bscript.decompile(txIn.script)
  var prevOutType = bscript.classifyInput(scriptSigChunks, true)

  function processScript (scriptType, scriptSigChunks, redeemScriptChunks) {
    // ensure chunks are decompiled
    scriptSigChunks = bscript.decompile(scriptSigChunks)
    redeemScriptChunks = redeemScriptChunks ? bscript.decompile(redeemScriptChunks) : undefined

    var hashType, pubKeys, signatures, prevOutScript, redeemScript, redeemScriptType, result, parsed

    switch (scriptType) {
      case 'scripthash':
        redeemScript = scriptSigChunks.slice(-1)[0]
        scriptSigChunks = bscript.compile(scriptSigChunks.slice(0, -1))

        redeemScriptType = bscript.classifyInput(scriptSigChunks, true)
        prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript))

        result = processScript(redeemScriptType, scriptSigChunks, bscript.decompile(redeemScript))

        result.prevOutScript = prevOutScript
        result.redeemScript = redeemScript
        result.redeemScriptType = redeemScriptType

        return result

      case 'pubkeyhash':
        parsed = ECSignature.parseScriptSignature(scriptSigChunks[0])
        hashType = parsed.hashType
        pubKeys = scriptSigChunks.slice(1)
        signatures = [parsed.signature]
        prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(pubKeys[0]))

        break

      case 'pubkey':
        parsed = ECSignature.parseScriptSignature(scriptSigChunks[0])
        hashType = parsed.hashType
        signatures = [parsed.signature]

        if (redeemScriptChunks) {
          pubKeys = redeemScriptChunks.slice(0, 1)
        }

        break

      case 'multisig':
        signatures = scriptSigChunks.slice(1).map(function (chunk) {
          if (chunk === ops.OP_0) return undefined

          parsed = ECSignature.parseScriptSignature(chunk)
          hashType = parsed.hashType

          return parsed.signature
        })

        if (redeemScriptChunks) {
          pubKeys = redeemScriptChunks.slice(1, -2)

          if (pubKeys.length !== signatures.length) {
            signatures = fixMSSignatures(transaction, vin, pubKeys, signatures, bscript.compile(redeemScriptChunks), hashType, redeemScript)
          }
        }

        break
    }

    return {
      hashType: hashType,
      pubKeys: pubKeys,
      signatures: signatures,
      prevOutScript: prevOutScript,
      redeemScript: redeemScript,
      redeemScriptType: redeemScriptType
    }
  }

  // Extract hashType, pubKeys, signatures and prevOutScript
  var result = processScript(prevOutType, scriptSigChunks)

  return {
    hashType: result.hashType,
    prevOutScript: result.prevOutScript,
    prevOutType: prevOutType,
    pubKeys: result.pubKeys,
    redeemScript: result.redeemScript,
    redeemScriptType: result.redeemScriptType,
    signatures: result.signatures
  }
}

function extractFromOutputScript (outputScript, kpPubKey) {
  var scriptType = bscript.classifyOutput(outputScript)
  var outputScriptChunks = bscript.decompile(outputScript)

  switch (scriptType) {
    case 'pubkeyhash':
      var pkh1 = outputScriptChunks[2]
      var pkh2 = bcrypto.hash160(kpPubKey)

      if (!bufferEquals(pkh1, pkh2)) throw new Error('privateKey cannot sign for this input')

      return {
        pubKeys: [kpPubKey],
        scriptType: scriptType
      }

    case 'pubkey':
      return {
        pubKeys: outputScriptChunks.slice(0, 1),
        scriptType: scriptType
      }

    case 'multisig':
      return {
        pubKeys: outputScriptChunks.slice(1, -2),
        scriptType: scriptType
      }
  }
}

function TransactionBuilder (network) {
  this.prevTxMap = {}
  this.network = network || networks.bitcoin

  this.inputs = []
  this.tx = new Transaction()
}

TransactionBuilder.prototype.setLockTime = function (locktime) {
  typeforce(types.UInt32, locktime)

  // if any signatures exist, throw
  if (this.inputs.some(function (input) {
    if (!input.signatures) return false

    return input.signatures.some(function (s) { return s })
  })) {
    throw new Error('No, this would invalidate signatures')
  }

  this.tx.locktime = locktime
}

TransactionBuilder.prototype.setVersion = function (version) {
  typeforce(types.UInt32, version)

  // XXX: this might eventually become more complex depending on what the versions represent
  this.tx.version = version
}

TransactionBuilder.fromTransaction = function (transaction, network) {
  var txb = new TransactionBuilder(network)

  // Copy other transaction fields
  txb.tx.version = transaction.version
  txb.tx.locktime = transaction.locktime

  // Extract/add inputs
  transaction.ins.forEach(function (txIn) {
    txb.addInput(txIn.hash, txIn.index, txIn.sequence)
  })

  // Extract/add outputs
  transaction.outs.forEach(function (txOut) {
    txb.addOutput(txOut.script, txOut.value)
  })

  // Extract/add signatures
  txb.inputs = transaction.ins.map(function (txIn, vin) {
    // TODO: verify whether extractInput is sane with coinbase scripts
    if (Transaction.isCoinbaseHash(txIn.hash)) {
      throw new Error('coinbase inputs not supported')
    }

    return extractInput(transaction, txIn, vin)
  })

  return txb
}

TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOutScript) {
  // is it a hex string?
  if (typeof txHash === 'string') {
    // transaction hashs's are displayed in reverse order, un-reverse it
    txHash = bufferReverse(new Buffer(txHash, 'hex'))

  // is it a Transaction object?
  } else if (txHash instanceof Transaction) {
    prevOutScript = txHash.outs[vout].script
    txHash = txHash.getHash()
  }

  var input = {}
  if (prevOutScript) {
    var prevOutScriptChunks = bscript.decompile(prevOutScript)
    var prevOutType = bscript.classifyOutput(prevOutScriptChunks)

    // if we can, extract pubKey information
    switch (prevOutType) {
      case 'multisig':
        input.pubKeys = prevOutScriptChunks.slice(1, -2)
        input.signatures = input.pubKeys.map(function () { return undefined })

        break

      case 'pubkey':
        input.pubKeys = prevOutScriptChunks.slice(0, 1)
        input.signatures = [undefined]

        break
    }

    if (prevOutType !== 'scripthash') {
      input.scriptType = prevOutType
    }

    input.prevOutScript = prevOutScript
    input.prevOutType = prevOutType
  }

  // if signatures exist, adding inputs is only acceptable if SIGHASH_ANYONECANPAY is used
  // throw if any signatures *didn't* use SIGHASH_ANYONECANPAY
  if (!this.inputs.every(function (otherInput) {
    // no signature
    if (otherInput.hashType === undefined) return true

    return otherInput.hashType & Transaction.SIGHASH_ANYONECANPAY
  })) {
    throw new Error('No, this would invalidate signatures')
  }

  var prevTxOut = txHash.toString('hex') + ':' + vout
  if (this.prevTxMap[prevTxOut]) throw new Error('Duplicate TxOut: ' + prevTxOut)

  var vin = this.tx.addInput(txHash, vout, sequence)
  this.inputs[vin] = input
  this.prevTxMap[prevTxOut] = vin

  return vin
}

TransactionBuilder.prototype.addOutput = function (scriptPubKey, value) {
  var nOutputs = this.tx.outs.length

  // if signatures exist, adding outputs is only acceptable if SIGHASH_NONE or SIGHASH_SINGLE is used
  // throws if any signatures didn't use SIGHASH_NONE|SIGHASH_SINGLE
  if (!this.inputs.every(function (input, index) {
    // no signature
    if (input.hashType === undefined) return true

    var hashTypeMod = input.hashType & 0x1f
    if (hashTypeMod === Transaction.SIGHASH_NONE) return true
    if (hashTypeMod === Transaction.SIGHASH_SINGLE) {
      // account for SIGHASH_SINGLE signing of a non-existing output, aka the "SIGHASH_SINGLE" bug
      return index < nOutputs
    }

    return false
  })) {
    throw new Error('No, this would invalidate signatures')
  }

  // Attempt to get a script if it's a base58 address string
  if (typeof scriptPubKey === 'string') {
    scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network)
  }

  return this.tx.addOutput(scriptPubKey, value)
}

TransactionBuilder.prototype.build = function () {
  return this.__build(false)
}
TransactionBuilder.prototype.buildIncomplete = function () {
  return this.__build(true)
}

var canBuildTypes = {
  'multisig': true,
  'pubkey': true,
  'pubkeyhash': true
}

function buildFromInputData (input, scriptType, parentType, redeemScript, allowIncomplete) {
  var scriptSig

  switch (scriptType) {
    case 'pubkeyhash':
      var pkhSignature = input.signatures[0].toScriptSignature(input.hashType)
      scriptSig = bscript.pubKeyHashInput(pkhSignature, input.pubKeys[0])
      break

    case 'pubkey':
      var pkSignature = input.signatures[0].toScriptSignature(input.hashType)
      scriptSig = bscript.pubKeyInput(pkSignature)
      break

    case 'multisig':
      var msSignatures = input.signatures.map(function (signature) {
        return signature && signature.toScriptSignature(input.hashType)
      })

      // fill in blanks with OP_0
      if (allowIncomplete) {
        for (var i = 0; i < msSignatures.length; ++i) {
          msSignatures[i] = msSignatures[i] || ops.OP_0
        }

      // remove blank signatures
      } else {
        msSignatures = msSignatures.filter(function (x) { return x })
      }

      scriptSig = bscript.multisigInput(msSignatures, allowIncomplete ? undefined : redeemScript)
      break
  }

  // wrap as scriptHash if necessary
  if (parentType === 'scripthash') {
    scriptSig = bscript.scriptHashInput(scriptSig, redeemScript)
  }

  return scriptSig
}

TransactionBuilder.prototype.__build = function (allowIncomplete) {
  if (!allowIncomplete) {
    if (!this.tx.ins.length) throw new Error('Transaction has no inputs')
    if (!this.tx.outs.length) throw new Error('Transaction has no outputs')
  }

  var tx = this.tx.clone()

  // Create script signatures from inputs
  this.inputs.forEach(function (input, index) {
    var scriptType = input.redeemScriptType || input.prevOutType
    var scriptSig

    if (!allowIncomplete) {
      if (!scriptType) throw new Error('Transaction is not complete')
      if (!canBuildTypes[scriptType]) throw new Error(scriptType + ' not supported')

      // XXX: only relevant to types that need signatures
      if (!input.signatures) throw new Error('Transaction is missing signatures')
    }

    if (input.signatures) {
      scriptSig = buildFromInputData(input, scriptType, input.prevOutType, input.redeemScript, allowIncomplete)
    }

    // did we build a scriptSig? Buffer('') is allowed
    if (scriptSig) {
      tx.setInputScript(index, scriptSig)
    }
  })

  return tx
}

TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hashType) {
  if (keyPair.network !== this.network) throw new Error('Inconsistent network')
  if (!this.inputs[index]) throw new Error('No input at index: ' + index)
  hashType = hashType || Transaction.SIGHASH_ALL

  var input = this.inputs[index]
  var canSign = input.hashType &&
    input.prevOutScript &&
    input.prevOutType &&
    input.pubKeys &&
    input.redeemScriptType &&
    input.signatures &&
    input.signatures.length === input.pubKeys.length

  var kpPubKey = keyPair.getPublicKeyBuffer()

  // are we ready to sign?
  if (canSign) {
    // if redeemScript was provided, enforce consistency
    if (redeemScript) {
      if (!bufferEquals(input.redeemScript, redeemScript)) throw new Error('Inconsistent redeemScript')
    }

    if (input.hashType !== hashType) throw new Error('Inconsistent hashType')

  // no? prepare
  } else {
    // must be pay-to-scriptHash?
    if (redeemScript) {
      // if we have a prevOutScript, enforce scriptHash equality to the redeemScript
      if (input.prevOutScript) {
        if (input.prevOutType !== 'scripthash') throw new Error('PrevOutScript must be P2SH')

        var scriptHash = bscript.decompile(input.prevOutScript)[1]
        if (!bufferEquals(scriptHash, bcrypto.hash160(redeemScript))) throw new Error('RedeemScript does not match ' + scriptHash.toString('hex'))
      }

      var extracted = extractFromOutputScript(redeemScript, kpPubKey)
      if (!extracted) throw new Error('RedeemScript not supported "' + bscript.toASM(redeemScript) + '"')

      // if we don't have a prevOutScript, generate a P2SH script
      if (!input.prevOutScript) {
        input.prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript))
        input.prevOutType = 'scripthash'
      }

      input.pubKeys = extracted.pubKeys
      input.redeemScript = redeemScript
      input.redeemScriptType = extracted.scriptType
      input.signatures = extracted.pubKeys.map(function () { return undefined })
    } else {
      // pay-to-scriptHash is not possible without a redeemScript
      if (input.prevOutType === 'scripthash') throw new Error('PrevOutScript is P2SH, missing redeemScript')

      // if we don't have a scriptType, assume pubKeyHash otherwise
      if (!input.scriptType) {
        input.prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(kpPubKey))
        input.prevOutType = 'pubkeyhash'

        input.pubKeys = [kpPubKey]
        input.scriptType = input.prevOutType
        input.signatures = [undefined]
      } else {
        // throw if we can't sign with it
        if (!input.pubKeys || !input.signatures) throw new Error(input.scriptType + ' not supported')
      }
    }

    input.hashType = hashType
  }

  // ready to sign?
  var signatureScript = input.redeemScript || input.prevOutScript
  var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType)

  // enforce in order signing of public keys
  var valid = input.pubKeys.some(function (pubKey, i) {
    if (!bufferEquals(kpPubKey, pubKey)) return false
    if (input.signatures[i]) throw new Error('Signature already exists')

    input.signatures[i] = keyPair.sign(signatureHash)

    return true
  })

  if (!valid) throw new Error('Key pair cannot sign for this input')
}

module.exports = TransactionBuilder
<commit_msg>TransactionBuilder: less exits<commit_after>var baddress = require('./address')
var bcrypto = require('./crypto')
var bscript = require('./script')
var bufferEquals = require('buffer-equals')
var bufferReverse = require('buffer-reverse')
var networks = require('./networks')
var ops = require('./opcodes.json')
var typeforce = require('typeforce')
var types = require('./types')

var ECPair = require('./ecpair')
var ECSignature = require('./ecsignature')
var Transaction = require('./transaction')

// re-orders signatures to match pubKeys, fills undefined otherwise
function fixMSSignatures (transaction, vin, pubKeys, signatures, prevOutScript, hashType, skipPubKey) {
  // maintain a local copy of unmatched signatures
  var unmatched = signatures.slice()
  var signatureHash

  return pubKeys.map(function (pubKey) {
    // skip optionally provided pubKey
    if (skipPubKey && bufferEquals(skipPubKey, pubKey)) return undefined

    var matched
    var keyPair2 = ECPair.fromPublicKeyBuffer(pubKey)

    // check for a matching signature
    unmatched.some(function (signature, i) {
      // skip if undefined || OP_0
      if (!signature) return false

      if (!signatureHash) {
        signatureHash = transaction.hashForSignature(vin, prevOutScript, hashType)
      }
      if (!keyPair2.verify(signatureHash, signature)) return false

      // remove matched signature from unmatched
      unmatched[i] = undefined
      matched = signature

      return true
    })

    return matched || undefined
  })
}

function extractInput (transaction, txIn, vin) {
  if (txIn.script.length === 0) return {}

  var scriptSigChunks = bscript.decompile(txIn.script)
  var prevOutType = bscript.classifyInput(scriptSigChunks, true)

  function processScript (scriptType, scriptSigChunks, redeemScriptChunks) {
    // ensure chunks are decompiled
    scriptSigChunks = bscript.decompile(scriptSigChunks)
    redeemScriptChunks = redeemScriptChunks ? bscript.decompile(redeemScriptChunks) : undefined

    var hashType, pubKeys, signatures, prevOutScript, redeemScript, redeemScriptType, result, parsed

    switch (scriptType) {
      case 'scripthash':
        redeemScript = scriptSigChunks.slice(-1)[0]
        scriptSigChunks = bscript.compile(scriptSigChunks.slice(0, -1))

        redeemScriptType = bscript.classifyInput(scriptSigChunks, true)
        prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript))

        result = processScript(redeemScriptType, scriptSigChunks, bscript.decompile(redeemScript))

        result.prevOutScript = prevOutScript
        result.redeemScript = redeemScript
        result.redeemScriptType = redeemScriptType

        return result

      case 'pubkeyhash':
        parsed = ECSignature.parseScriptSignature(scriptSigChunks[0])
        hashType = parsed.hashType
        pubKeys = scriptSigChunks.slice(1)
        signatures = [parsed.signature]
        prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(pubKeys[0]))

        break

      case 'pubkey':
        parsed = ECSignature.parseScriptSignature(scriptSigChunks[0])
        hashType = parsed.hashType
        signatures = [parsed.signature]

        if (redeemScriptChunks) {
          pubKeys = redeemScriptChunks.slice(0, 1)
        }

        break

      case 'multisig':
        signatures = scriptSigChunks.slice(1).map(function (chunk) {
          if (chunk === ops.OP_0) return undefined

          parsed = ECSignature.parseScriptSignature(chunk)
          hashType = parsed.hashType

          return parsed.signature
        })

        if (redeemScriptChunks) {
          pubKeys = redeemScriptChunks.slice(1, -2)

          if (pubKeys.length !== signatures.length) {
            signatures = fixMSSignatures(transaction, vin, pubKeys, signatures, bscript.compile(redeemScriptChunks), hashType, redeemScript)
          }
        }

        break
    }

    return {
      hashType: hashType,
      pubKeys: pubKeys,
      signatures: signatures,
      prevOutScript: prevOutScript,
      redeemScript: redeemScript,
      redeemScriptType: redeemScriptType
    }
  }

  // Extract hashType, pubKeys, signatures and prevOutScript
  var result = processScript(prevOutType, scriptSigChunks)

  return {
    hashType: result.hashType,
    prevOutScript: result.prevOutScript,
    prevOutType: prevOutType,
    pubKeys: result.pubKeys,
    redeemScript: result.redeemScript,
    redeemScriptType: result.redeemScriptType,
    signatures: result.signatures
  }
}

function extractFromOutputScript (outputScript, kpPubKey) {
  var scriptType = bscript.classifyOutput(outputScript)
  var outputScriptChunks = bscript.decompile(outputScript)

  var pubKeys
  switch (scriptType) {
    case 'pubkeyhash':
      var pkh1 = outputScriptChunks[2]
      var pkh2 = bcrypto.hash160(kpPubKey)

      if (!bufferEquals(pkh1, pkh2)) throw new Error('privateKey cannot sign for this input')
      pubKeys = [kpPubKey]
      break

    case 'pubkey':
      pubKeys = outputScriptChunks.slice(0, 1)
      break

    case 'multisig':
      pubKeys = outputScriptChunks.slice(1, -2)
      break

    default: return
  }

  return {
    pubKeys: pubKeys,
    scriptType: scriptType
  }
}

function TransactionBuilder (network) {
  this.prevTxMap = {}
  this.network = network || networks.bitcoin

  this.inputs = []
  this.tx = new Transaction()
}

TransactionBuilder.prototype.setLockTime = function (locktime) {
  typeforce(types.UInt32, locktime)

  // if any signatures exist, throw
  if (this.inputs.some(function (input) {
    if (!input.signatures) return false

    return input.signatures.some(function (s) { return s })
  })) {
    throw new Error('No, this would invalidate signatures')
  }

  this.tx.locktime = locktime
}

TransactionBuilder.prototype.setVersion = function (version) {
  typeforce(types.UInt32, version)

  // XXX: this might eventually become more complex depending on what the versions represent
  this.tx.version = version
}

TransactionBuilder.fromTransaction = function (transaction, network) {
  var txb = new TransactionBuilder(network)

  // Copy other transaction fields
  txb.tx.version = transaction.version
  txb.tx.locktime = transaction.locktime

  // Extract/add inputs
  transaction.ins.forEach(function (txIn) {
    txb.addInput(txIn.hash, txIn.index, txIn.sequence)
  })

  // Extract/add outputs
  transaction.outs.forEach(function (txOut) {
    txb.addOutput(txOut.script, txOut.value)
  })

  // Extract/add signatures
  txb.inputs = transaction.ins.map(function (txIn, vin) {
    // TODO: verify whether extractInput is sane with coinbase scripts
    if (Transaction.isCoinbaseHash(txIn.hash)) {
      throw new Error('coinbase inputs not supported')
    }

    return extractInput(transaction, txIn, vin)
  })

  return txb
}

TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOutScript) {
  // is it a hex string?
  if (typeof txHash === 'string') {
    // transaction hashs's are displayed in reverse order, un-reverse it
    txHash = bufferReverse(new Buffer(txHash, 'hex'))

  // is it a Transaction object?
  } else if (txHash instanceof Transaction) {
    prevOutScript = txHash.outs[vout].script
    txHash = txHash.getHash()
  }

  var input = {}
  if (prevOutScript) {
    var prevOutScriptChunks = bscript.decompile(prevOutScript)
    var prevOutType = bscript.classifyOutput(prevOutScriptChunks)

    // if we can, extract pubKey information
    switch (prevOutType) {
      case 'multisig':
        input.pubKeys = prevOutScriptChunks.slice(1, -2)
        input.signatures = input.pubKeys.map(function () { return undefined })

        break

      case 'pubkey':
        input.pubKeys = prevOutScriptChunks.slice(0, 1)
        input.signatures = [undefined]

        break
    }

    if (prevOutType !== 'scripthash') {
      input.scriptType = prevOutType
    }

    input.prevOutScript = prevOutScript
    input.prevOutType = prevOutType
  }

  // if signatures exist, adding inputs is only acceptable if SIGHASH_ANYONECANPAY is used
  // throw if any signatures *didn't* use SIGHASH_ANYONECANPAY
  if (!this.inputs.every(function (otherInput) {
    // no signature
    if (otherInput.hashType === undefined) return true

    return otherInput.hashType & Transaction.SIGHASH_ANYONECANPAY
  })) {
    throw new Error('No, this would invalidate signatures')
  }

  var prevTxOut = txHash.toString('hex') + ':' + vout
  if (this.prevTxMap[prevTxOut]) throw new Error('Duplicate TxOut: ' + prevTxOut)

  var vin = this.tx.addInput(txHash, vout, sequence)
  this.inputs[vin] = input
  this.prevTxMap[prevTxOut] = vin

  return vin
}

TransactionBuilder.prototype.addOutput = function (scriptPubKey, value) {
  var nOutputs = this.tx.outs.length

  // if signatures exist, adding outputs is only acceptable if SIGHASH_NONE or SIGHASH_SINGLE is used
  // throws if any signatures didn't use SIGHASH_NONE|SIGHASH_SINGLE
  if (!this.inputs.every(function (input, index) {
    // no signature
    if (input.hashType === undefined) return true

    var hashTypeMod = input.hashType & 0x1f
    if (hashTypeMod === Transaction.SIGHASH_NONE) return true
    if (hashTypeMod === Transaction.SIGHASH_SINGLE) {
      // account for SIGHASH_SINGLE signing of a non-existing output, aka the "SIGHASH_SINGLE" bug
      return index < nOutputs
    }

    return false
  })) {
    throw new Error('No, this would invalidate signatures')
  }

  // Attempt to get a script if it's a base58 address string
  if (typeof scriptPubKey === 'string') {
    scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network)
  }

  return this.tx.addOutput(scriptPubKey, value)
}

TransactionBuilder.prototype.build = function () {
  return this.__build(false)
}
TransactionBuilder.prototype.buildIncomplete = function () {
  return this.__build(true)
}

var canBuildTypes = {
  'multisig': true,
  'pubkey': true,
  'pubkeyhash': true
}

function buildFromInputData (input, scriptType, parentType, redeemScript, allowIncomplete) {
  var scriptSig

  switch (scriptType) {
    case 'pubkeyhash':
      var pkhSignature = input.signatures[0].toScriptSignature(input.hashType)
      scriptSig = bscript.pubKeyHashInput(pkhSignature, input.pubKeys[0])
      break

    case 'pubkey':
      var pkSignature = input.signatures[0].toScriptSignature(input.hashType)
      scriptSig = bscript.pubKeyInput(pkSignature)
      break

    case 'multisig':
      var msSignatures = input.signatures.map(function (signature) {
        return signature && signature.toScriptSignature(input.hashType)
      })

      // fill in blanks with OP_0
      if (allowIncomplete) {
        for (var i = 0; i < msSignatures.length; ++i) {
          msSignatures[i] = msSignatures[i] || ops.OP_0
        }

      // remove blank signatures
      } else {
        msSignatures = msSignatures.filter(function (x) { return x })
      }

      scriptSig = bscript.multisigInput(msSignatures, allowIncomplete ? undefined : redeemScript)
      break
  }

  // wrap as scriptHash if necessary
  if (parentType === 'scripthash') {
    scriptSig = bscript.scriptHashInput(scriptSig, redeemScript)
  }

  return scriptSig
}

TransactionBuilder.prototype.__build = function (allowIncomplete) {
  if (!allowIncomplete) {
    if (!this.tx.ins.length) throw new Error('Transaction has no inputs')
    if (!this.tx.outs.length) throw new Error('Transaction has no outputs')
  }

  var tx = this.tx.clone()

  // Create script signatures from inputs
  this.inputs.forEach(function (input, index) {
    var scriptType = input.redeemScriptType || input.prevOutType
    var scriptSig

    if (!allowIncomplete) {
      if (!scriptType) throw new Error('Transaction is not complete')
      if (!canBuildTypes[scriptType]) throw new Error(scriptType + ' not supported')

      // XXX: only relevant to types that need signatures
      if (!input.signatures) throw new Error('Transaction is missing signatures')
    }

    if (input.signatures) {
      scriptSig = buildFromInputData(input, scriptType, input.prevOutType, input.redeemScript, allowIncomplete)
    }

    // did we build a scriptSig? Buffer('') is allowed
    if (scriptSig) {
      tx.setInputScript(index, scriptSig)
    }
  })

  return tx
}

TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hashType) {
  if (keyPair.network !== this.network) throw new Error('Inconsistent network')
  if (!this.inputs[index]) throw new Error('No input at index: ' + index)
  hashType = hashType || Transaction.SIGHASH_ALL

  var input = this.inputs[index]
  var canSign = input.hashType &&
    input.prevOutScript &&
    input.prevOutType &&
    input.pubKeys &&
    input.redeemScriptType &&
    input.signatures &&
    input.signatures.length === input.pubKeys.length

  var kpPubKey = keyPair.getPublicKeyBuffer()

  // are we ready to sign?
  if (canSign) {
    // if redeemScript was provided, enforce consistency
    if (redeemScript) {
      if (!bufferEquals(input.redeemScript, redeemScript)) throw new Error('Inconsistent redeemScript')
    }

    if (input.hashType !== hashType) throw new Error('Inconsistent hashType')

  // no? prepare
  } else {
    // must be pay-to-scriptHash?
    if (redeemScript) {
      // if we have a prevOutScript, enforce scriptHash equality to the redeemScript
      if (input.prevOutScript) {
        if (input.prevOutType !== 'scripthash') throw new Error('PrevOutScript must be P2SH')

        var scriptHash = bscript.decompile(input.prevOutScript)[1]
        if (!bufferEquals(scriptHash, bcrypto.hash160(redeemScript))) throw new Error('RedeemScript does not match ' + scriptHash.toString('hex'))
      }

      var extracted = extractFromOutputScript(redeemScript, kpPubKey)
      if (!extracted) throw new Error('RedeemScript not supported "' + bscript.toASM(redeemScript) + '"')

      // if we don't have a prevOutScript, generate a P2SH script
      if (!input.prevOutScript) {
        input.prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript))
        input.prevOutType = 'scripthash'
      }

      input.pubKeys = extracted.pubKeys
      input.redeemScript = redeemScript
      input.redeemScriptType = extracted.scriptType
      input.signatures = extracted.pubKeys.map(function () { return undefined })
    } else {
      // pay-to-scriptHash is not possible without a redeemScript
      if (input.prevOutType === 'scripthash') throw new Error('PrevOutScript is P2SH, missing redeemScript')

      // if we don't have a scriptType, assume pubKeyHash otherwise
      if (!input.scriptType) {
        input.prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(kpPubKey))
        input.prevOutType = 'pubkeyhash'

        input.pubKeys = [kpPubKey]
        input.scriptType = input.prevOutType
        input.signatures = [undefined]
      } else {
        // throw if we can't sign with it
        if (!input.pubKeys || !input.signatures) throw new Error(input.scriptType + ' not supported')
      }
    }

    input.hashType = hashType
  }

  // ready to sign?
  var signatureScript = input.redeemScript || input.prevOutScript
  var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType)

  // enforce in order signing of public keys
  var valid = input.pubKeys.some(function (pubKey, i) {
    if (!bufferEquals(kpPubKey, pubKey)) return false
    if (input.signatures[i]) throw new Error('Signature already exists')

    input.signatures[i] = keyPair.sign(signatureHash)

    return true
  })

  if (!valid) throw new Error('Key pair cannot sign for this input')
}

module.exports = TransactionBuilder
<|endoftext|>


<commit_before>(function() {
  'use strict';

  angular.module('kct.services.persistence.saveManager')
    .service('ModVersionDepsService', [
      '$state',
      '$stateParams',
      '$filter',
      '$q',
      '$firebaseObject',
      '$firebaseArray',
      'ModRef',
      'ModVersionRef',
      'ModVersionDepRef',
      'BiDirModVersionDepRef',
      ModVersionDepsService
    ])
  ;

  function ModVersionDepsService(
    $state,
    $stateParams,
    $filter,
    $q,
    $firebaseObject,
    $firebaseArray,
    ModRef,
    ModVersionRef,
    ModVersionDepRef,
    BiDirModVersionDepRef
  ) {
    return {
      addModTitleToDeps   : addModTitleToDeps,
      addModVersionDep    : addModVersionDep,
      editVersionDep      : editVersionDep,
      removeModVersionDep : removeModVersionDep
    };

    function addModTitleToDeps(modVersionDeps) {
      _.forEach(modVersionDeps, function(mod) {
        mod.title = ' ';
        var obj = $firebaseObject(new ModRef(mod.$id));
        obj.$watch(function() {
          mod.title = obj.title;
        });
      });
    }

    function addModVersionDep(newDependence) {
      console.log(newDependence);
      var deferred = $q.defer(),
          existingModVersion;

      newDependence.minVersion = $filter('replaceChars')(newDependence.minVersion, '.', '_');

      existingModVersion = $firebaseObject(new ModVersionRef(newDependence.$id, newDependence.minVersion));
      existingModVersion.$loaded(function() {
        if (existingModVersion.$value === null) {
          existingModVersion.desc = '';
          existingModVersion.$save();
        }
      });

      newDependence.$save().then(function() {
        var biDirModVersion = _getVersionInfoFromDep(newDependence),
            diDirDep = $firebaseObject(
              new BiDirModVersionDepRef(
                newDependence.$id,
                newDependence.minVersion
              ).child(biDirModVersion.modId)
            );

        diDirDep.$loaded(function() {
          diDirDep[biDirModVersion.modVersionId] = true;
          diDirDep.$save().then(function() {
            deferred.resolve();
          });
        });
      });
      return deferred.promise;
    }

    function editVersionDep(editedDependence) {
      var deferred = $q.defer();

      var actualDep = $firebaseObject(new ModVersionDepRef($stateParams.modId, $stateParams.modVersionId, editedDependence.$id));
      actualDep.$loaded(function() {
        editedDependence.minVersion = $filter('replaceChars')(editedDependence.minVersion, '.', '_');
        if (editedDependence.minVersion !== actualDep.minVersion) {
          removeModVersionDep(actualDep).then(addModVersionDep(editedDependence)).then(function() {
            $state.reload();
            deferred.resolve();
          });
        } else {
          editedDependence.$save().then(function() {
            deferred.resolve();
          });
        }
      });

      return deferred.promise;
    }

    function removeModVersionDep(removedDependence) {
      var deferredBiDirRemoved = $q.defer(),
          deferredRemoved = $q.defer(),
          biDirModVersion = _getVersionInfoFromDep(removedDependence),
          diDirDep = $firebaseObject(
            new BiDirModVersionDepRef(
              removedDependence.$id,
              removedDependence.minVersion
            ).child(biDirModVersion.modId)
          )
      ;
      removedDependence.$remove().then(function() {
        deferredRemoved.resolve();
      });
      diDirDep.$loaded(function() {
        diDirDep[biDirModVersion.modVersionId] = null;
        diDirDep.$save().then(function() {
          deferredBiDirRemoved.resolve();
        });
      });
      return $q.all([deferredRemoved.promise, deferredBiDirRemoved.promise]);
    }

    function _getVersionInfoFromDep(newDependence) {
      var tempRef = newDependence.$ref().parent();

      return {
        modId        : tempRef.parent().key(),
        modVersionId : tempRef.key()
      };
    }

  }

})();
<commit_msg>unused log<commit_after>(function() {
  'use strict';

  angular.module('kct.services.persistence.saveManager')
    .service('ModVersionDepsService', [
      '$state',
      '$stateParams',
      '$filter',
      '$q',
      '$firebaseObject',
      '$firebaseArray',
      'ModRef',
      'ModVersionRef',
      'ModVersionDepRef',
      'BiDirModVersionDepRef',
      ModVersionDepsService
    ])
  ;

  function ModVersionDepsService(
    $state,
    $stateParams,
    $filter,
    $q,
    $firebaseObject,
    $firebaseArray,
    ModRef,
    ModVersionRef,
    ModVersionDepRef,
    BiDirModVersionDepRef
  ) {
    return {
      addModTitleToDeps   : addModTitleToDeps,
      addModVersionDep    : addModVersionDep,
      editVersionDep      : editVersionDep,
      removeModVersionDep : removeModVersionDep
    };

    function addModTitleToDeps(modVersionDeps) {
      _.forEach(modVersionDeps, function(mod) {
        mod.title = ' ';
        var obj = $firebaseObject(new ModRef(mod.$id));
        obj.$watch(function() {
          mod.title = obj.title;
        });
      });
    }

    function addModVersionDep(newDependence) {
      var deferred = $q.defer(),
          existingModVersion;

      newDependence.minVersion = $filter('replaceChars')(newDependence.minVersion, '.', '_');

      existingModVersion = $firebaseObject(new ModVersionRef(newDependence.$id, newDependence.minVersion));
      existingModVersion.$loaded(function() {
        if (existingModVersion.$value === null) {
          existingModVersion.desc = '';
          existingModVersion.$save();
        }
      });

      newDependence.$save().then(function() {
        var biDirModVersion = _getVersionInfoFromDep(newDependence),
            diDirDep = $firebaseObject(
              new BiDirModVersionDepRef(
                newDependence.$id,
                newDependence.minVersion
              ).child(biDirModVersion.modId)
            );

        diDirDep.$loaded(function() {
          diDirDep[biDirModVersion.modVersionId] = true;
          diDirDep.$save().then(function() {
            deferred.resolve();
          });
        });
      });
      return deferred.promise;
    }

    function editVersionDep(editedDependence) {
      var deferred = $q.defer();

      var actualDep = $firebaseObject(new ModVersionDepRef($stateParams.modId, $stateParams.modVersionId, editedDependence.$id));
      actualDep.$loaded(function() {
        editedDependence.minVersion = $filter('replaceChars')(editedDependence.minVersion, '.', '_');
        if (editedDependence.minVersion !== actualDep.minVersion) {
          removeModVersionDep(actualDep).then(addModVersionDep(editedDependence)).then(function() {
            $state.reload();
            deferred.resolve();
          });
        } else {
          editedDependence.$save().then(function() {
            deferred.resolve();
          });
        }
      });

      return deferred.promise;
    }

    function removeModVersionDep(removedDependence) {
      var deferredBiDirRemoved = $q.defer(),
          deferredRemoved = $q.defer(),
          biDirModVersion = _getVersionInfoFromDep(removedDependence),
          diDirDep = $firebaseObject(
            new BiDirModVersionDepRef(
              removedDependence.$id,
              removedDependence.minVersion
            ).child(biDirModVersion.modId)
          )
      ;
      removedDependence.$remove().then(function() {
        deferredRemoved.resolve();
      });
      diDirDep.$loaded(function() {
        diDirDep[biDirModVersion.modVersionId] = null;
        diDirDep.$save().then(function() {
          deferredBiDirRemoved.resolve();
        });
      });
      return $q.all([deferredRemoved.promise, deferredBiDirRemoved.promise]);
    }

    function _getVersionInfoFromDep(newDependence) {
      var tempRef = newDependence.$ref().parent();

      return {
        modId        : tempRef.parent().key(),
        modVersionId : tempRef.key()
      };
    }

  }

})();
<|endoftext|>


<commit_before>var bignum = require('bignum'),
  utils = require('../lib/utils'),
  Block = require('../lib/block.js');

var testUtils = exports;

/**
 * toDecimal - converts buffer to decimal string, no leading zeroes
 * @param  {Buffer}
 * @return {String}
 */
exports.toDecimal = function (buffer) {
  return bignum.fromBuffer(buffer).toString();
};

/**
 * fromDecimal - converts decimal string to buffer
 * @param {String}
*  @return {Buffer}
 */
exports.fromDecimal = function (string) {
  return utils.intToBuffer(parseInt(string, 10));
};

/**
 * address - converts 0x to utils.zero256, otherwise returns input
 * @param  {String}
 * @return {String}
 */
exports.address = function (string) {
  return !parseInt(string, 16) ? utils.zero256() : string;
};

exports.makeBlockFromEnv = function (env) {
  var block = new Block();
  block.header.timestamp = testUtils.fromDecimal(env.currentTimestamp);
  block.header.gasLimit = testUtils.fromDecimal(env.currentGasLimit);
  block.header.parentHash = new Buffer(env.previousHash, 'hex');
  block.header.coinbase = new Buffer(env.currentCoinbase, 'hex');
  block.header.difficulty = testUtils.fromDecimal(env.currentDifficulty);
  block.header.number = testUtils.fromDecimal(env.currentNumber);

  return block;
};

exports.makeRunCodeData = function (exec, account, block) {
  return {
    account: account,
    origin: new Buffer(exec.origin, 'hex'),
    code:  new Buffer(exec.code.slice(2), 'hex'),  // slice off 0x
    value: testUtils.fromDecimal(exec.value),
    address: new Buffer(exec.address, 'hex'),
    from: new Buffer(exec.caller, 'hex'),
    data:  new Buffer(exec.data.slice(2), 'hex'),  // slice off 0x
    gasLimit: exec.gas,
    block: block
  };
};
<commit_msg>jsdoc<commit_after>var bignum = require('bignum'),
  utils = require('../lib/utils'),
  Block = require('../lib/block.js');

var testUtils = exports;

/**
 * toDecimal - converts buffer to decimal string, no leading zeroes
 * @param  {Buffer}
 * @return {String}
 */
exports.toDecimal = function (buffer) {
  return bignum.fromBuffer(buffer).toString();
};

/**
 * fromDecimal - converts decimal string to buffer
 * @param {String}
*  @return {Buffer}
 */
exports.fromDecimal = function (string) {
  return utils.intToBuffer(parseInt(string, 10));
};

/**
 * address - converts 0x to utils.zero256, otherwise returns input
 * @param  {String}
 * @return {String}
 */
exports.address = function (string) {
  return !parseInt(string, 16) ? utils.zero256() : string;
};

/**
 * makeBlockFromEnv - helper to create a block from the env object in tests repo
 * @param {Object} env object from tests repo
 */
exports.makeBlockFromEnv = function (env) {
  var block = new Block();
  block.header.timestamp = testUtils.fromDecimal(env.currentTimestamp);
  block.header.gasLimit = testUtils.fromDecimal(env.currentGasLimit);
  block.header.parentHash = new Buffer(env.previousHash, 'hex');
  block.header.coinbase = new Buffer(env.currentCoinbase, 'hex');
  block.header.difficulty = testUtils.fromDecimal(env.currentDifficulty);
  block.header.number = testUtils.fromDecimal(env.currentNumber);

  return block;
};

/**
 * makeRunCodeData - helper to create the object for VM.runCode using
 *   the exec object specified in the tests repo
 * @param {Object} exec    object from the tests repo
 * @param {[type]} account that the executing code belongs to
 * @param {[type]} block   that the transaction belongs to
 */
exports.makeRunCodeData = function (exec, account, block) {
  return {
    account: account,
    origin: new Buffer(exec.origin, 'hex'),
    code:  new Buffer(exec.code.slice(2), 'hex'),  // slice off 0x
    value: testUtils.fromDecimal(exec.value),
    address: new Buffer(exec.address, 'hex'),
    from: new Buffer(exec.caller, 'hex'),
    data:  new Buffer(exec.data.slice(2), 'hex'),  // slice off 0x
    gasLimit: exec.gas,
    block: block
  };
};
<|endoftext|>


<commit_before>var assert = require("assert");
require("../runtime/dev");

function check(g, yields, returnValue) {
  for (var i = 0; i < yields.length; ++i) {
    var info = g.next(i);
    assert.deepEqual(info.value, yields[i]);
    assert.strictEqual(info.done, false);
  }

  assert.deepEqual(g.next(i), {
    value: returnValue,
    done: true
  });
}

describe("wrapGenerator", function() {
  it("should be defined globally", function() {
    var global = Function("return this")();
    assert.ok("wrapGenerator" in global);
    assert.strictEqual(global.wrapGenerator, wrapGenerator);
  });

  it("should be a function", function() {
    assert.strictEqual(typeof wrapGenerator, "function");
  });
});

describe("simple argument yielder", function() {
  it("should yield only its first argument", function() {
    function *gen(x) {
      yield x;
    }

    check(gen("oyez"), ["oyez"]);
    check(gen("foo", "bar"), ["foo"]);
  });
});

describe("range generator", function() {
  function *range(n) {
    for (var i = 0; i < n; ++i) {
      yield i;
    }
  }

  it("should yield the empty range", function() {
    check(range(0), []);
  })

  it("should yield the range 0..n-1", function() {
    check(range(5), [0, 1, 2, 3, 4]);
  });
});

describe("collatz generator", function() {
  function *gen(n) {
    var count = 0;

    yield n;

    while (n !== 1) {
      count += 1;

      if (n % 2) {
        yield n = n * 3 + 1;
      } else {
        yield n >>= 1;
      }
    }

    return count;
  }

  function collatz(n) {
    var result = [n];

    while (n !== 1) {
      if (n % 2) {
        n *= 3;
        n += 1;
      } else {
        n >>= 1;
      }

      result.push(n);
    }

    return result;
  }

  var seven = collatz(7);
  var fiftyTwo = seven.slice(seven.indexOf(52));
  var eightyTwo = collatz(82);

  it("seven", function() {
    check(gen(7), seven, 16);
  });

  it("fifty two", function() {
    check(gen(52), fiftyTwo, 11);
  });

  it("eighty two", function() {
    check(gen(82), eightyTwo, 110);
  });
});

describe("try-catch generator", function() {
  function *gen(x) {
    yield 0;
    try {
      yield 1;
      if (x % 2 === 0)
        throw 2;
      yield x;
    } catch (x) {
      yield x;
    }
    yield 3;
  }

  it("should catch exceptions properly", function() {
    check(gen(4), [0, 1, 2, 3]);
    check(gen(5), [0, 1, 5, 3]);
  });
});

describe("try-finally generator", function() {
  function *gen(condition) {
    yield 0;
    try {
      yield 1;
      throw 2;
      yield 3;
    } finally {
      if (condition) {
        yield 4;
        return 5;
      }
      yield 6;
      return 7;
    }
  }

  it("should execute finally blocks", function() {
    check(gen(true), [0, 1, 4], 5);
    check(gen(false), [0, 1, 6], 7);
  });
});

describe("try-catch-finally generator", function() {
  function *gen() {
    yield 0;
    try {
      try {
        yield 1;
        throw 2;
        yield 3;
      } catch (x) {
        throw yield x;
      } finally {
        yield 5;
      }
    } catch (thrown) {
      yield thrown;
    }
    yield 6;
  }

  it("should catch and then finalize", function() {
    check(gen(), [0, 1, 2, 3, 6]);
  });
});

describe("dynamic exception", function() {
  function *gen(x, fname) {
    try {
      return fns[fname](x);
    } catch (thrown) {
      yield thrown;
    }
  }

  var fns = {
    f: function(x) {
      throw x;
    },

    g: function(x) {
      return x;
    }
  };

  it("should be dispatched correctly", function() {
    check(gen("asdf", "f"), ["asdf"]);
    check(gen("asdf", "g"), [], "asdf");
  });
});

describe("nested finally blocks", function() {
  function *gen() {
    try {
      try {
        try {
          throw "thrown";
        } finally {
          yield 1;
        }
      } catch (thrown) {
        yield thrown;
      } finally {
        yield 2;
      }
    } finally {
      yield 3;
    }
  }

  it("should execute in order", function() {
    check(gen(), [1, "thrown", 2, 3]);
  });
});
<commit_msg>Add a test of yielding in a for-in loop.<commit_after>var assert = require("assert");
require("../runtime/dev");

function check(g, yields, returnValue) {
  for (var i = 0; i < yields.length; ++i) {
    var info = g.next(i);
    assert.deepEqual(info.value, yields[i]);
    assert.strictEqual(info.done, false);
  }

  assert.deepEqual(g.next(i), {
    value: returnValue,
    done: true
  });
}

describe("wrapGenerator", function() {
  it("should be defined globally", function() {
    var global = Function("return this")();
    assert.ok("wrapGenerator" in global);
    assert.strictEqual(global.wrapGenerator, wrapGenerator);
  });

  it("should be a function", function() {
    assert.strictEqual(typeof wrapGenerator, "function");
  });
});

describe("simple argument yielder", function() {
  it("should yield only its first argument", function() {
    function *gen(x) {
      yield x;
    }

    check(gen("oyez"), ["oyez"]);
    check(gen("foo", "bar"), ["foo"]);
  });
});

describe("range generator", function() {
  function *range(n) {
    for (var i = 0; i < n; ++i) {
      yield i;
    }
  }

  it("should yield the empty range", function() {
    check(range(0), []);
  })

  it("should yield the range 0..n-1", function() {
    check(range(5), [0, 1, 2, 3, 4]);
  });
});

describe("collatz generator", function() {
  function *gen(n) {
    var count = 0;

    yield n;

    while (n !== 1) {
      count += 1;

      if (n % 2) {
        yield n = n * 3 + 1;
      } else {
        yield n >>= 1;
      }
    }

    return count;
  }

  function collatz(n) {
    var result = [n];

    while (n !== 1) {
      if (n % 2) {
        n *= 3;
        n += 1;
      } else {
        n >>= 1;
      }

      result.push(n);
    }

    return result;
  }

  var seven = collatz(7);
  var fiftyTwo = seven.slice(seven.indexOf(52));
  var eightyTwo = collatz(82);

  it("seven", function() {
    check(gen(7), seven, 16);
  });

  it("fifty two", function() {
    check(gen(52), fiftyTwo, 11);
  });

  it("eighty two", function() {
    check(gen(82), eightyTwo, 110);
  });
});

describe("try-catch generator", function() {
  function *gen(x) {
    yield 0;
    try {
      yield 1;
      if (x % 2 === 0)
        throw 2;
      yield x;
    } catch (x) {
      yield x;
    }
    yield 3;
  }

  it("should catch exceptions properly", function() {
    check(gen(4), [0, 1, 2, 3]);
    check(gen(5), [0, 1, 5, 3]);
  });
});

describe("try-finally generator", function() {
  function *gen(condition) {
    yield 0;
    try {
      yield 1;
      throw 2;
      yield 3;
    } finally {
      if (condition) {
        yield 4;
        return 5;
      }
      yield 6;
      return 7;
    }
  }

  it("should execute finally blocks", function() {
    check(gen(true), [0, 1, 4], 5);
    check(gen(false), [0, 1, 6], 7);
  });
});

describe("try-catch-finally generator", function() {
  function *gen() {
    yield 0;
    try {
      try {
        yield 1;
        throw 2;
        yield 3;
      } catch (x) {
        throw yield x;
      } finally {
        yield 5;
      }
    } catch (thrown) {
      yield thrown;
    }
    yield 6;
  }

  it("should catch and then finalize", function() {
    check(gen(), [0, 1, 2, 3, 6]);
  });
});

describe("dynamic exception", function() {
  function *gen(x, fname) {
    try {
      return fns[fname](x);
    } catch (thrown) {
      yield thrown;
    }
  }

  var fns = {
    f: function(x) {
      throw x;
    },

    g: function(x) {
      return x;
    }
  };

  it("should be dispatched correctly", function() {
    check(gen("asdf", "f"), ["asdf"]);
    check(gen("asdf", "g"), [], "asdf");
  });
});

describe("nested finally blocks", function() {
  function *gen() {
    try {
      try {
        try {
          throw "thrown";
        } finally {
          yield 1;
        }
      } catch (thrown) {
        yield thrown;
      } finally {
        yield 2;
      }
    } finally {
      yield 3;
    }
  }

  it("should execute in order", function() {
    check(gen(), [1, "thrown", 2, 3]);
  });
});

describe("for-in loop generator", function() {
  function *gen(obj) {
    var count = 0;
    for (var key in (yield "why not", obj)) {
      if (obj.hasOwnProperty(key)) {
        if (key === "skip") {
          break;
        }
        count += 1;
        yield [key, obj[key]];
      }
    }
    return count;
  }

  it("should visit properties until 'skip'", function() {
    check(
      gen({ a: 1, b: 2, skip: 3, c: 4 }),
      ["why not", ["a", 1], ["b", 2]], 2);
  });
});
<|endoftext|>